Completed
Push — composer-installed ( 5832b4 )
by Ilia
08:49
created

FirePHP::headersSent()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 2
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
// Authors:
3
// - cadorn, Christoph Dorn <[email protected]>, Copyright 2007, New BSD License
4
// - qbbr, Sokolov Innokenty <[email protected]>, Copyright 2011, New BSD License
5
// - cadorn, Christoph Dorn <[email protected]>, Copyright 2011, MIT License
6
7
/**
8
 * *** BEGIN LICENSE BLOCK *****
9
 * 
10
 * [MIT License](http://www.opensource.org/licenses/mit-license.php)
11
 * 
12
 * Copyright (c) 2007+ [Christoph Dorn](http://www.christophdorn.com/)
13
 * 
14
 * Permission is hereby granted, free of charge, to any person obtaining a copy
15
 * of this software and associated documentation files (the "Software"), to deal
16
 * in the Software without restriction, including without limitation the rights
17
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18
 * copies of the Software, and to permit persons to whom the Software is
19
 * furnished to do so, subject to the following conditions:
20
 *
21
 * The above copyright notice and this permission notice shall be included in
22
 * all copies or substantial portions of the Software.
23
 *
24
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
30
 * THE SOFTWARE.
31
 * 
32
 * ***** END LICENSE BLOCK *****
33
 * 
34
 * @copyright       Copyright (C) 2007+ Christoph Dorn
35
 * @author          Christoph Dorn <[email protected]>
36
 * @license         [MIT License](http://www.opensource.org/licenses/mit-license.php)
37
 * @package         FirePHPCore
38
 */
39
40
/**
41
 * @see http://code.google.com/p/firephp/issues/detail?id=112
42
 */
43
if (!defined('E_STRICT')) {
44
    define('E_STRICT', 2048);
45
}
46
if (!defined('E_RECOVERABLE_ERROR')) {
47
    define('E_RECOVERABLE_ERROR', 4096);
48
}
49
if (!defined('E_DEPRECATED')) {
50
    define('E_DEPRECATED', 8192);
51
}
52
if (!defined('E_USER_DEPRECATED')) {
53
    define('E_USER_DEPRECATED', 16384);
54
} 
55
 
56
/**
57
 * Sends the given data to the FirePHP Firefox Extension.
58
 * The data can be displayed in the Firebug Console or in the
59
 * "Server" request tab.
60
 * 
61
 * For more information see: http://www.firephp.org/
62
 * 
63
 * @copyright       Copyright (C) 2007+ Christoph Dorn
64
 * @author          Christoph Dorn <[email protected]>
65
 * @license         [MIT License](http://www.opensource.org/licenses/mit-license.php)
66
 * @package         FirePHPCore
67
 *
68
 * @deprecated 2.3 This will be removed in Minify 3.0
69
 */
70
class FirePHP {
71
72
    /**
73
     * FirePHP version
74
     *
75
     * @var string
76
     */
77
    const VERSION = '0.3';    // @pinf replace '0.3' with '%%VERSION%%'
78
79
    /**
80
     * Firebug LOG level
81
     *
82
     * Logs a message to firebug console.
83
     * 
84
     * @var string
85
     */
86
    const LOG = 'LOG';
87
  
88
    /**
89
     * Firebug INFO level
90
     *
91
     * Logs a message to firebug console and displays an info icon before the message.
92
     * 
93
     * @var string
94
     */
95
    const INFO = 'INFO';
96
    
97
    /**
98
     * Firebug WARN level
99
     *
100
     * Logs a message to firebug console, displays an warning icon before the message and colors the line turquoise.
101
     * 
102
     * @var string
103
     */
104
    const WARN = 'WARN';
105
    
106
    /**
107
     * Firebug ERROR level
108
     *
109
     * Logs a message to firebug console, displays an error icon before the message and colors the line yellow. Also increments the firebug error count.
110
     * 
111
     * @var string
112
     */
113
    const ERROR = 'ERROR';
114
    
115
    /**
116
     * Dumps a variable to firebug's server panel
117
     *
118
     * @var string
119
     */
120
    const DUMP = 'DUMP';
121
    
122
    /**
123
     * Displays a stack trace in firebug console
124
     *
125
     * @var string
126
     */
127
    const TRACE = 'TRACE';
128
    
129
    /**
130
     * Displays an exception in firebug console
131
     * 
132
     * Increments the firebug error count.
133
     *
134
     * @var string
135
     */
136
    const EXCEPTION = 'EXCEPTION';
137
    
138
    /**
139
     * Displays an table in firebug console
140
     *
141
     * @var string
142
     */
143
    const TABLE = 'TABLE';
144
    
145
    /**
146
     * Starts a group in firebug console
147
     * 
148
     * @var string
149
     */
150
    const GROUP_START = 'GROUP_START';
151
    
152
    /**
153
     * Ends a group in firebug console
154
     * 
155
     * @var string
156
     */
157
    const GROUP_END = 'GROUP_END';
158
    
159
    /**
160
     * Singleton instance of FirePHP
161
     *
162
     * @var FirePHP
163
     */
164
    protected static $instance = null;
165
    
166
    /**
167
     * Flag whether we are logging from within the exception handler
168
     * 
169
     * @var boolean
170
     */
171
    protected $inExceptionHandler = false;
172
    
173
    /**
174
     * Flag whether to throw PHP errors that have been converted to ErrorExceptions
175
     * 
176
     * @var boolean
177
     */
178
    protected $throwErrorExceptions = true;
179
    
180
    /**
181
     * Flag whether to convert PHP assertion errors to Exceptions
182
     * 
183
     * @var boolean
184
     */
185
    protected $convertAssertionErrorsToExceptions = true;
186
    
187
    /**
188
     * Flag whether to throw PHP assertion errors that have been converted to Exceptions
189
     * 
190
     * @var boolean
191
     */
192
    protected $throwAssertionExceptions = false;
193
194
    /**
195
     * Wildfire protocol message index
196
     *
197
     * @var integer
198
     */
199
    protected $messageIndex = 1;
200
    
201
    /**
202
     * Options for the library
203
     * 
204
     * @var array
205
     */
206
    protected $options = array('maxDepth' => 10,
207
                               'maxObjectDepth' => 5,
208
                               'maxArrayDepth' => 5,
209
                               'useNativeJsonEncode' => true,
210
                               'includeLineNumbers' => true);
211
212
    /**
213
     * Filters used to exclude object members when encoding
214
     * 
215
     * @var array
216
     */
217
    protected $objectFilters = array(
218
        'firephp' => array('objectStack', 'instance', 'json_objectStack'),
219
        'firephp_test_class' => array('objectStack', 'instance', 'json_objectStack')
220
    );
221
222
    /**
223
     * A stack of objects used to detect recursion during object encoding
224
     * 
225
     * @var object
226
     */
227
    protected $objectStack = array();
228
229
    /**
230
     * Flag to enable/disable logging
231
     * 
232
     * @var boolean
233
     */
234
    protected $enabled = true;
235
236
    /**
237
     * The insight console to log to if applicable
238
     * 
239
     * @var object
240
     */
241
    protected $logToInsightConsole = null;
242
243
    /**
244
     * When the object gets serialized only include specific object members.
245
     * 
246
     * @return array
247
     */  
248
    public function __sleep()
249
    {
250
        return array('options', 'objectFilters', 'enabled');
251
    }
252
    
253
    /**
254
     * Gets singleton instance of FirePHP
255
     *
256
     * @param boolean $autoCreate
257
     * @return FirePHP
258
     */
259
    public static function getInstance($autoCreate = false)
260
    {
261
        if ($autoCreate === true && !self::$instance) {
262
            self::init();
263
        }
264
        return self::$instance;
265
    }
266
    
267
    /**
268
     * Creates FirePHP object and stores it for singleton access
269
     *
270
     * @return FirePHP
271
     */
272
    public static function init()
273
    {
274
        return self::setInstance(new self());
0 ignored issues
show
Deprecated Code introduced by
The class FirePHP has been deprecated with message: 2.3 This will be removed in Minify 3.0

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
275
    }
276
277
    /**
278
     * Set the instance of the FirePHP singleton
279
     * 
280
     * @param FirePHP $instance The FirePHP object instance
281
     * @return FirePHP
282
     */
283
    public static function setInstance($instance)
284
    {
285
        return self::$instance = $instance;
286
    }
287
288
    /**
289
     * Set an Insight console to direct all logging calls to
290
     * 
291
     * @param object $console The console object to log to
292
     * @return void
293
     */
294
    public function setLogToInsightConsole($console)
295
    {
296
        if (is_string($console)) {
297
            if (get_class($this) != 'FirePHP_Insight' && !is_subclass_of($this, 'FirePHP_Insight')) {
298
                throw new Exception('FirePHP instance not an instance or subclass of FirePHP_Insight!');
299
            }
300
            $this->logToInsightConsole = $this->to('request')->console($console);
301
        } else {
302
            $this->logToInsightConsole = $console;
303
        }
304
    }
305
306
    /**
307
     * Enable and disable logging to Firebug
308
     * 
309
     * @param boolean $enabled TRUE to enable, FALSE to disable
310
     * @return void
311
     */
312
    public function setEnabled($enabled)
313
    {
314
       $this->enabled = $enabled;
315
    }
316
    
317
    /**
318
     * Check if logging is enabled
319
     * 
320
     * @return boolean TRUE if enabled
321
     */
322
    public function getEnabled()
323
    {
324
        return $this->enabled;
325
    }
326
    
327
    /**
328
     * Specify a filter to be used when encoding an object
329
     * 
330
     * Filters are used to exclude object members.
331
     * 
332
     * @param string $class The class name of the object
333
     * @param array $filter An array of members to exclude
334
     * @return void
335
     */
336
    public function setObjectFilter($class, $filter)
337
    {
338
        $this->objectFilters[strtolower($class)] = $filter;
339
    }
340
  
341
    /**
342
     * Set some options for the library
343
     * 
344
     * Options:
345
     *  - maxDepth: The maximum depth to traverse (default: 10)
346
     *  - maxObjectDepth: The maximum depth to traverse objects (default: 5)
347
     *  - maxArrayDepth: The maximum depth to traverse arrays (default: 5)
348
     *  - useNativeJsonEncode: If true will use json_encode() (default: true)
349
     *  - includeLineNumbers: If true will include line numbers and filenames (default: true)
350
     * 
351
     * @param array $options The options to be set
352
     * @return void
353
     */
354
    public function setOptions($options)
355
    {
356
        $this->options = array_merge($this->options, $options);
357
    }
358
359
    /**
360
     * Get options from the library
361
     *
362
     * @return array The currently set options
363
     */
364
    public function getOptions()
365
    {
366
        return $this->options;
367
    }
368
369
    /**
370
     * Set an option for the library
371
     * 
372
     * @param string $name
373
     * @param mixed $value
374
     * @return void
375
     * @throws Exception
376
     */  
377 View Code Duplication
    public function setOption($name, $value)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
378
    {
379
        if (!isset($this->options[$name])) {
380
            throw $this->newException('Unknown option: ' . $name);
381
        }
382
        $this->options[$name] = $value;
383
    }
384
385
    /**
386
     * Get an option from the library
387
     *
388
     * @param string $name
389
     * @return mixed
390
     * @throws Exception
391
     */
392 View Code Duplication
    public function getOption($name)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
393
    {
394
        if (!isset($this->options[$name])) {
395
            throw $this->newException('Unknown option: ' . $name);
396
        }
397
        return $this->options[$name];
398
    }
399
400
    /**
401
     * Register FirePHP as your error handler
402
     * 
403
     * Will throw exceptions for each php error.
404
     * 
405
     * @return mixed Returns a string containing the previously defined error handler (if any)
406
     */
407
    public function registerErrorHandler($throwErrorExceptions = false)
408
    {
409
        //NOTE: The following errors will not be caught by this error handler:
410
        //      E_ERROR, E_PARSE, E_CORE_ERROR,
411
        //      E_CORE_WARNING, E_COMPILE_ERROR,
412
        //      E_COMPILE_WARNING, E_STRICT
413
    
414
        $this->throwErrorExceptions = $throwErrorExceptions;
415
    
416
        return set_error_handler(array($this, 'errorHandler'));
417
    }
418
419
    /**
420
     * FirePHP's error handler
421
     * 
422
     * Throws exception for each php error that will occur.
423
     *
424
     * @param integer $errno
425
     * @param string $errstr
426
     * @param string $errfile
427
     * @param integer $errline
428
     * @param array $errcontext
429
     */
430
    public function errorHandler($errno, $errstr, $errfile, $errline, $errcontext)
431
    {
432
        // Don't throw exception if error reporting is switched off
433
        if (error_reporting() == 0) {
434
            return;
435
        }
436
        // Only throw exceptions for errors we are asking for
437
        if (error_reporting() & $errno) {
438
439
            $exception = new ErrorException($errstr, 0, $errno, $errfile, $errline);
440
            if ($this->throwErrorExceptions) {
441
                throw $exception;
442
            } else {
443
                $this->fb($exception);
444
            }
445
        }
446
    }
447
  
448
    /**
449
     * Register FirePHP as your exception handler
450
     * 
451
     * @return mixed Returns the name of the previously defined exception handler,
452
     *               or NULL on error.
453
     *               If no previous handler was defined, NULL is also returned.
454
     */
455
    public function registerExceptionHandler()
456
    {
457
        return set_exception_handler(array($this, 'exceptionHandler'));
458
    }
459
  
460
    /**
461
     * FirePHP's exception handler
462
     * 
463
     * Logs all exceptions to your firebug console and then stops the script.
464
     *
465
     * @param Exception $exception
466
     * @throws Exception
467
     */
468
    function exceptionHandler($exception)
469
    {
470
        $this->inExceptionHandler = true;
471
    
472
        header('HTTP/1.1 500 Internal Server Error');
473
    
474
        try {
475
            $this->fb($exception);
476
        } catch (Exception $e) {
477
            echo 'We had an exception: ' . $e;
478
        }
479
        
480
        $this->inExceptionHandler = false;
481
    }
482
  
483
    /**
484
     * Register FirePHP driver as your assert callback
485
     * 
486
     * @param boolean $convertAssertionErrorsToExceptions
487
     * @param boolean $throwAssertionExceptions
488
     * @return mixed Returns the original setting or FALSE on errors
489
     */
490
    public function registerAssertionHandler($convertAssertionErrorsToExceptions = true, $throwAssertionExceptions = false)
491
    {
492
        $this->convertAssertionErrorsToExceptions = $convertAssertionErrorsToExceptions;
493
        $this->throwAssertionExceptions = $throwAssertionExceptions;
494
        
495
        if ($throwAssertionExceptions && !$convertAssertionErrorsToExceptions) {
496
            throw $this->newException('Cannot throw assertion exceptions as assertion errors are not being converted to exceptions!');
497
        }
498
        
499
        return assert_options(ASSERT_CALLBACK, array($this, 'assertionHandler'));
500
    }
501
  
502
    /**
503
     * FirePHP's assertion handler
504
     *
505
     * Logs all assertions to your firebug console and then stops the script.
506
     *
507
     * @param string $file File source of assertion
508
     * @param integer $line Line source of assertion
509
     * @param mixed $code Assertion code
510
     */
511
    public function assertionHandler($file, $line, $code)
512
    {
513
        if ($this->convertAssertionErrorsToExceptions) {
514
          
515
          $exception = new ErrorException('Assertion Failed - Code[ ' . $code . ' ]', 0, null, $file, $line);
516
    
517
          if ($this->throwAssertionExceptions) {
518
              throw $exception;
519
          } else {
520
              $this->fb($exception);
521
          }
522
        
523
        } else {
524
            $this->fb($code, 'Assertion Failed', FirePHP::ERROR, array('File' => $file, 'Line' => $line));
525
        }
526
    }
527
  
528
    /**
529
     * Start a group for following messages.
530
     * 
531
     * Options:
532
     *   Collapsed: [true|false]
533
     *   Color:     [#RRGGBB|ColorName]
534
     *
535
     * @param string $name
536
     * @param array $options OPTIONAL Instructions on how to log the group
537
     * @return true
538
     * @throws Exception
539
     */
540
    public function group($name, $options = null)
541
    {
542
    
543
        if ( !isset($name) ) {
544
            throw $this->newException('You must specify a label for the group!');
545
        }
546
547
        if ($options) {
548
            if (!is_array($options)) {
549
                throw $this->newException('Options must be defined as an array!');
550
            }
551
            if (array_key_exists('Collapsed', $options)) {
552
                $options['Collapsed'] = ($options['Collapsed']) ? 'true' : 'false';
553
            }
554
        }
555
556
        return $this->fb(null, $name, FirePHP::GROUP_START, $options);
557
    }
558
  
559
    /**
560
     * Ends a group you have started before
561
     *
562
     * @return true
563
     * @throws Exception
564
     */
565
    public function groupEnd()
566
    {
567
        return $this->fb(null, null, FirePHP::GROUP_END);
568
    }
569
570
    /**
571
     * Log object with label to firebug console
572
     *
573
     * @see FirePHP::LOG
574
     * @param mixes $object
575
     * @param string $label
576
     * @return true
577
     * @throws Exception
578
     */
579
    public function log($object, $label = null, $options = array())
580
    {
581
        return $this->fb($object, $label, FirePHP::LOG, $options);
582
    } 
583
584
    /**
585
     * Log object with label to firebug console
586
     *
587
     * @see FirePHP::INFO
588
     * @param mixes $object
589
     * @param string $label
590
     * @return true
591
     * @throws Exception
592
     */
593
    public function info($object, $label = null, $options = array())
594
    {
595
        return $this->fb($object, $label, FirePHP::INFO, $options);
596
    } 
597
598
    /**
599
     * Log object with label to firebug console
600
     *
601
     * @see FirePHP::WARN
602
     * @param mixes $object
603
     * @param string $label
604
     * @return true
605
     * @throws Exception
606
     */
607
    public function warn($object, $label = null, $options = array())
608
    {
609
        return $this->fb($object, $label, FirePHP::WARN, $options);
610
    } 
611
612
    /**
613
     * Log object with label to firebug console
614
     *
615
     * @see FirePHP::ERROR
616
     * @param mixes $object
617
     * @param string $label
618
     * @return true
619
     * @throws Exception
620
     */
621
    public function error($object, $label = null, $options = array())
622
    {
623
        return $this->fb($object, $label, FirePHP::ERROR, $options);
624
    } 
625
626
    /**
627
     * Dumps key and variable to firebug server panel
628
     *
629
     * @see FirePHP::DUMP
630
     * @param string $key
631
     * @param mixed $variable
632
     * @return true
633
     * @throws Exception
634
     */
635
    public function dump($key, $variable, $options = array())
636
    {
637
        if (!is_string($key)) {
638
            throw $this->newException('Key passed to dump() is not a string');
639
        }
640
        if (strlen($key) > 100) {
641
            throw $this->newException('Key passed to dump() is longer than 100 characters');
642
        }
643
        if (!preg_match_all('/^[a-zA-Z0-9-_\.:]*$/', $key, $m)) {
644
            throw $this->newException('Key passed to dump() contains invalid characters [a-zA-Z0-9-_\.:]');
645
        }
646
        return $this->fb($variable, $key, FirePHP::DUMP, $options);
647
    }
648
  
649
    /**
650
     * Log a trace in the firebug console
651
     *
652
     * @see FirePHP::TRACE
653
     * @param string $label
654
     * @return true
655
     * @throws Exception
656
     */
657
    public function trace($label)
658
    {
659
        return $this->fb($label, FirePHP::TRACE);
660
    } 
661
662
    /**
663
     * Log a table in the firebug console
664
     *
665
     * @see FirePHP::TABLE
666
     * @param string $label
667
     * @param string $table
668
     * @return true
669
     * @throws Exception
670
     */
671
    public function table($label, $table, $options = array())
672
    {
673
        return $this->fb($table, $label, FirePHP::TABLE, $options);
674
    }
675
676
    /**
677
     * Insight API wrapper
678
     * 
679
     * @see Insight_Helper::to()
680
     */
681 View Code Duplication
    public static function to()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
682
    {
683
        $instance = self::getInstance();
684
        if (!method_exists($instance, '_to')) {
685
            throw new Exception('FirePHP::to() implementation not loaded');
686
        }
687
        $args = func_get_args();
688
        return call_user_func_array(array($instance, '_to'), $args);
689
    }
690
691
    /**
692
     * Insight API wrapper
693
     * 
694
     * @see Insight_Helper::plugin()
695
     */
696 View Code Duplication
    public static function plugin()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
697
    {
698
        $instance = self::getInstance();
699
        if (!method_exists($instance, '_plugin')) {
700
            throw new Exception('FirePHP::plugin() implementation not loaded');
701
        }
702
        $args = func_get_args();
703
        return call_user_func_array(array($instance, '_plugin'), $args);
704
    }
705
706
    /**
707
     * Check if FirePHP is installed on client
708
     *
709
     * @return boolean
710
     */
711
    public function detectClientExtension()
712
    {
713
        // Check if FirePHP is installed on client via User-Agent header
714
        if (@preg_match_all('/\sFirePHP\/([\.\d]*)\s?/si', $this->getUserAgent(), $m) &&
715
           version_compare($m[1][0], '0.0.6', '>=')) {
716
            return true;
717
        } else
718
        // Check if FirePHP is installed on client via X-FirePHP-Version header
719
        if (@preg_match_all('/^([\.\d]*)$/si', $this->getRequestHeader('X-FirePHP-Version'), $m) &&
720
           version_compare($m[1][0], '0.0.6', '>=')) {
721
            return true;
722
        }
723
        return false;
724
    }
725
 
726
    /**
727
     * Log varible to Firebug
728
     * 
729
     * @see http://www.firephp.org/Wiki/Reference/Fb
730
     * @param mixed $object The variable to be logged
731
     * @return boolean Return TRUE if message was added to headers, FALSE otherwise
732
     * @throws Exception
733
     */
734
    public function fb($object)
735
    {
736
        if ($this instanceof FirePHP_Insight && method_exists($this, '_logUpgradeClientMessage')) {
0 ignored issues
show
Bug introduced by
The class FirePHP_Insight does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
737
            if (!FirePHP_Insight::$upgradeClientMessageLogged) { // avoid infinite recursion as _logUpgradeClientMessage() logs a message
738
                $this->_logUpgradeClientMessage();
0 ignored issues
show
Bug introduced by
The method _logUpgradeClientMessage() does not seem to exist on object<FirePHP>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
739
            }
740
        }
741
742
        static $insightGroupStack = array();
743
744
        if (!$this->getEnabled()) {
745
            return false;
746
        }
747
748
        if ($this->headersSent($filename, $linenum)) {
749
            // If we are logging from within the exception handler we cannot throw another exception
750
            if ($this->inExceptionHandler) {
751
                // Simply echo the error out to the page
752
                echo '<div style="border: 2px solid red; font-family: Arial; font-size: 12px; background-color: lightgray; padding: 5px;"><span style="color: red; font-weight: bold;">FirePHP ERROR:</span> Headers already sent in <b>' . $filename . '</b> on line <b>' . $linenum . '</b>. Cannot send log data to FirePHP. You must have Output Buffering enabled via ob_start() or output_buffering ini directive.</div>';
753
            } else {
754
                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.');
755
            }
756
        }
757
      
758
        $type = null;
759
        $label = null;
760
        $options = array();
761
      
762
        if (func_num_args() == 1) {
763
        } else if (func_num_args() == 2) {
764
            switch (func_get_arg(1)) {
765
                case self::LOG:
766
                case self::INFO:
767
                case self::WARN:
768
                case self::ERROR:
769
                case self::DUMP:
770
                case self::TRACE:
771
                case self::EXCEPTION:
772
                case self::TABLE:
773
                case self::GROUP_START:
774
                case self::GROUP_END:
775
                    $type = func_get_arg(1);
776
                    break;
777
                default:
778
                    $label = func_get_arg(1);
779
                    break;
780
            }
781
        } else if (func_num_args() == 3) {
782
            $type = func_get_arg(2);
783
            $label = func_get_arg(1);
784
        } else if (func_num_args() == 4) {
785
            $type = func_get_arg(2);
786
            $label = func_get_arg(1);
787
            $options = func_get_arg(3);
788
        } else {
789
            throw $this->newException('Wrong number of arguments to fb() function!');
790
        }
791
792
        // Get folder name where firephp is located.
793
        $parentFolder = basename(dirname(__FILE__));
794
        $parentFolderLength = strlen( $parentFolder );
795
        $fbLength = 7 + $parentFolderLength;
796
        $fireClassLength = 18 + $parentFolderLength;
797
798
        if ($this->logToInsightConsole !== null && (get_class($this) == 'FirePHP_Insight' || is_subclass_of($this, 'FirePHP_Insight'))) {
799
            $trace = debug_backtrace();
800
            if (!$trace) return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression $trace of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
801
            for ($i = 0; $i < sizeof($trace); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function sizeof() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
802
                if (isset($trace[$i]['class'])) {
803
                    if ($trace[$i]['class'] == 'FirePHP' || $trace[$i]['class'] == 'FB') {
804
                        continue;
805
                    }
806
                }
807
                if (isset($trace[$i]['file'])) {
808
                    $path = $this->_standardizePath($trace[$i]['file']);
809
                    if (substr($path, -1*$fbLength, $fbLength) == $parentFolder.'/fb.php' || substr($path, -1*$fireClassLength, $fireClassLength) == $parentFolder.'/FirePHP.class.php') {
810
                        continue;
811
                    }
812
                }
813 View Code Duplication
                if (isset($trace[$i]['function']) && $trace[$i]['function'] == 'fb' &&
814
                        isset($trace[$i - 1]['file']) && substr($this->_standardizePath($trace[$i - 1]['file']), -1*$fbLength, $fbLength) == $parentFolder.'/fb.php') {
815
                    continue;
816
                }
817 View Code Duplication
                if (isset($trace[$i]['class']) && $trace[$i]['class'] == 'FB' &&
818
                        isset($trace[$i - 1]['file']) && substr($this->_standardizePath($trace[$i - 1]['file']), -1*$fbLength, $fbLength) == $parentFolder.'/fb.php') {
819
                    continue;
820
                }
821
                break;
822
            }
823
            // adjust trace offset
824
            $msg = $this->logToInsightConsole->option('encoder.trace.offsetAdjustment', $i);
825
826
            if ($object instanceof Exception) {
827
                $type = self::EXCEPTION;
828
            }
829
            if ($label && $type != self::TABLE && $type != self::GROUP_START) {
830
                $msg = $msg->label($label);
831
            }
832
            switch ($type) {
833
                case self::DUMP:
834
                case self::LOG:
835
                    return $msg->log($object);
836
                case self::INFO:
837
                    return $msg->info($object);
838
                case self::WARN:
839
                    return $msg->warn($object);
840
                case self::ERROR:
841
                    return $msg->error($object);
842
                case self::TRACE:
843
                    return $msg->trace($object);
844
                case self::EXCEPTION:
845
                    return $this->plugin('error')->handleException($object, $msg);
846
                case self::TABLE:
847
                    if (isset($object[0]) && !is_string($object[0]) && $label) {
848
                        $object = array($label, $object);
849
                    }
850
                    return $msg->table($object[0], array_slice($object[1], 1), $object[1][0]);
851
                case self::GROUP_START:
852
                    $insightGroupStack[] = $msg->group(md5($label))->open();
853
                    return $msg->log($label);
854
                case self::GROUP_END:
855
                    if (count($insightGroupStack) == 0) {
856
                        throw new Error('Too many groupEnd() as opposed to group() calls!');
857
                    }
858
                    $group = array_pop($insightGroupStack);
859
                    return $group->close();
860
                default:
861
                    return $msg->log($object);
862
            }
863
        }
864
865
        if (!$this->detectClientExtension()) {
866
            return false;
867
        }
868
869
        $meta = array();
870
        $skipFinalObjectEncode = false;
871
      
872
        if ($object instanceof Exception) {
873
    
874
            $meta['file'] = $this->_escapeTraceFile($object->getFile());
875
            $meta['line'] = $object->getLine();
876
          
877
            $trace = $object->getTrace();
878
            if ($object instanceof ErrorException
879
               && isset($trace[0]['function'])
880
               && $trace[0]['function'] == 'errorHandler'
881
               && isset($trace[0]['class'])
882
               && $trace[0]['class'] == 'FirePHP') {
883
               
884
                $severity = false;
885
                switch ($object->getSeverity()) {
886
                    case E_WARNING:
887
                        $severity = 'E_WARNING';
888
                        break;
889
890
                    case E_NOTICE:
891
                        $severity = 'E_NOTICE';
892
                        break;
893
894
                    case E_USER_ERROR:
895
                        $severity = 'E_USER_ERROR';
896
                        break;
897
898
                    case E_USER_WARNING:
899
                        $severity = 'E_USER_WARNING';
900
                        break;
901
902
                    case E_USER_NOTICE:
903
                        $severity = 'E_USER_NOTICE';
904
                        break;
905
906
                    case E_STRICT:
907
                        $severity = 'E_STRICT';
908
                        break;
909
910
                    case E_RECOVERABLE_ERROR:
911
                        $severity = 'E_RECOVERABLE_ERROR';
912
                        break;
913
914
                    case E_DEPRECATED:
915
                        $severity = 'E_DEPRECATED';
916
                        break;
917
918
                    case E_USER_DEPRECATED:
919
                        $severity = 'E_USER_DEPRECATED';
920
                        break;
921
                }
922
                   
923
                $object = array('Class' => get_class($object),
924
                                'Message' => $severity . ': ' . $object->getMessage(),
925
                                'File' => $this->_escapeTraceFile($object->getFile()),
926
                                'Line' => $object->getLine(),
927
                                'Type' => 'trigger',
928
                                'Trace' => $this->_escapeTrace(array_splice($trace, 2)));
929
                $skipFinalObjectEncode = true;
930
            } else {
931
                $object = array('Class' => get_class($object),
932
                                'Message' => $object->getMessage(),
933
                                'File' => $this->_escapeTraceFile($object->getFile()),
934
                                'Line' => $object->getLine(),
935
                                'Type' => 'throw',
936
                                'Trace' => $this->_escapeTrace($trace));
937
                $skipFinalObjectEncode = true;
938
            }
939
            $type = self::EXCEPTION;
940
          
941
        } else if ($type == self::TRACE) {
942
          
943
            $trace = debug_backtrace();
944
            if (!$trace) return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression $trace of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
945
            for ($i = 0; $i < sizeof($trace); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function sizeof() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
946
    
947
                if (isset($trace[$i]['class'])
948
                   && isset($trace[$i]['file'])
949
                   && ($trace[$i]['class'] == 'FirePHP'
950
                       || $trace[$i]['class'] == 'FB')
951
                   && (substr($this->_standardizePath($trace[$i]['file']), -1*$fbLength, $fbLength) == $parentFolder.'/fb.php'
952
                       || substr($this->_standardizePath($trace[$i]['file']), -1*$fireClassLength, $fireClassLength) == $parentFolder.'/FirePHP.class.php')) {
953
                    /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */
954
                } else
955
                if (isset($trace[$i]['class'])
956
                   && isset($trace[$i+1]['file'])
957
                   && $trace[$i]['class'] == 'FirePHP'
958
                   && substr($this->_standardizePath($trace[$i + 1]['file']), -1*$fbLength, $fbLength) == $parentFolder.'/fb.php') {
959
                    /* Skip fb() */
960
                } else
961
                if ($trace[$i]['function'] == 'fb'
962
                   || $trace[$i]['function'] == 'trace'
963
                   || $trace[$i]['function'] == 'send') {
964
965
                    $object = array('Class' => isset($trace[$i]['class']) ? $trace[$i]['class'] : '',
966
                                    'Type' => isset($trace[$i]['type']) ? $trace[$i]['type'] : '',
967
                                    'Function' => isset($trace[$i]['function']) ? $trace[$i]['function'] : '',
968
                                    'Message' => $trace[$i]['args'][0],
969
                                    'File' => isset($trace[$i]['file']) ? $this->_escapeTraceFile($trace[$i]['file']) : '',
970
                                    'Line' => isset($trace[$i]['line']) ? $trace[$i]['line'] : '',
971
                                    'Args' => isset($trace[$i]['args']) ? $this->encodeObject($trace[$i]['args']) : '',
972
                                    'Trace' => $this->_escapeTrace(array_splice($trace, $i + 1)));
973
        
974
                    $skipFinalObjectEncode = true;
975
                    $meta['file'] = isset($trace[$i]['file']) ? $this->_escapeTraceFile($trace[$i]['file']) : '';
976
                    $meta['line'] = isset($trace[$i]['line']) ? $trace[$i]['line'] : '';
977
                    break;
978
                }
979
            }
980
    
981
        } else
982
        if ($type == self::TABLE) {
983
          
984
            if (isset($object[0]) && is_string($object[0])) {
985
                $object[1] = $this->encodeTable($object[1]);
986
            } else {
987
                $object = $this->encodeTable($object);
988
            }
989
    
990
            $skipFinalObjectEncode = true;
991
          
992
        } else if ($type == self::GROUP_START) {
993
          
994
            if (!$label) {
995
                throw $this->newException('You must specify a label for the group!');
996
            }
997
          
998
        } else {
999
            if ($type === null) {
1000
                $type = self::LOG;
1001
            }
1002
        }
1003
        
1004
        if ($this->options['includeLineNumbers']) {
1005
            if (!isset($meta['file']) || !isset($meta['line'])) {
1006
    
1007
                $trace = debug_backtrace();
1008
                for ($i = 0; $trace && $i < sizeof($trace); $i++) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $trace of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1009
          
1010
                    if (isset($trace[$i]['class'])
1011
                       && isset($trace[$i]['file'])
1012
                       && ($trace[$i]['class'] == 'FirePHP'
1013
                           || $trace[$i]['class'] == 'FB')
1014
                       && (substr($this->_standardizePath($trace[$i]['file']), -1*$fbLength, $fbLength) == $parentFolder.'/fb.php'
1015
                           || substr($this->_standardizePath($trace[$i]['file']), -1*$fireClassLength, $fireClassLength) == $parentFolder.'/FirePHP.class.php')) {
1016
                        /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */
1017
                    } else
1018
                    if (isset($trace[$i]['class'])
1019
                       && isset($trace[$i + 1]['file'])
1020
                       && $trace[$i]['class'] == 'FirePHP'
1021
                       && substr($this->_standardizePath($trace[$i + 1]['file']), -1*$fbLength, $fbLength) == $parentFolder.'/fb.php') {
1022
                        /* Skip fb() */
1023
                    } else
1024
                    if (isset($trace[$i]['file'])
1025
                       && substr($this->_standardizePath($trace[$i]['file']), -1*$fbLength, $fbLength) == $parentFolder.'/fb.php') {
1026
                        /* Skip FB::fb() */
1027
                    } else {
1028
                        $meta['file'] = isset($trace[$i]['file']) ? $this->_escapeTraceFile($trace[$i]['file']) : '';
1029
                        $meta['line'] = isset($trace[$i]['line']) ? $trace[$i]['line'] : '';
1030
                        break;
1031
                    }
1032
                }      
1033
            }
1034
        } else {
1035
            unset($meta['file']);
1036
            unset($meta['line']);
1037
        }
1038
1039
        $this->setHeader('X-Wf-Protocol-1', 'http://meta.wildfirehq.org/Protocol/JsonStream/0.2');
1040
        $this->setHeader('X-Wf-1-Plugin-1', 'http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/' . self::VERSION);
1041
     
1042
        $structureIndex = 1;
1043
        if ($type == self::DUMP) {
1044
            $structureIndex = 2;
1045
            $this->setHeader('X-Wf-1-Structure-2', 'http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1');
1046
        } else {
1047
            $this->setHeader('X-Wf-1-Structure-1', 'http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1');
1048
        }
1049
      
1050
        if ($type == self::DUMP) {
1051
            $msg = '{"' . $label . '":' . $this->jsonEncode($object, $skipFinalObjectEncode) . '}';
1052
        } else {
1053
            $msgMeta = $options;
1054
            $msgMeta['Type'] = $type;
1055
            if ($label !== null) {
1056
                $msgMeta['Label'] = $label;
1057
            }
1058 View Code Duplication
            if (isset($meta['file']) && !isset($msgMeta['File'])) {
1059
                $msgMeta['File'] = $meta['file'];
1060
            }
1061 View Code Duplication
            if (isset($meta['line']) && !isset($msgMeta['Line'])) {
1062
                $msgMeta['Line'] = $meta['line'];
1063
            }
1064
            $msg = '[' . $this->jsonEncode($msgMeta) . ',' . $this->jsonEncode($object, $skipFinalObjectEncode) . ']';
1065
        }
1066
        
1067
        $parts = explode("\n", chunk_split($msg, 5000, "\n"));
1068
1069
        for ($i = 0; $i < count($parts); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
1070
            
1071
            $part = $parts[$i];
1072
            if ($part) {
1073
                
1074
                if (count($parts) > 2) {
1075
                    // Message needs to be split into multiple parts
1076
                    $this->setHeader('X-Wf-1-' . $structureIndex . '-' . '1-' . $this->messageIndex,
1077
                                     (($i == 0) ? strlen($msg) : '')
1078
                                     . '|' . $part . '|'
1079
                                     . (($i < count($parts) - 2) ? '\\' : ''));
1080
                } else {
1081
                    $this->setHeader('X-Wf-1-' . $structureIndex . '-' . '1-' . $this->messageIndex,
1082
                                     strlen($part) . '|' . $part . '|');
1083
                }
1084
                
1085
                $this->messageIndex++;
1086
                
1087
                if ($this->messageIndex > 99999) {
1088
                    throw $this->newException('Maximum number (99,999) of messages reached!');             
1089
                }
1090
            }
1091
        }
1092
    
1093
        $this->setHeader('X-Wf-1-Index', $this->messageIndex - 1);
1094
    
1095
        return true;
1096
    }
1097
  
1098
    /**
1099
     * Standardizes path for windows systems.
1100
     *
1101
     * @param string $path
1102
     * @return string
1103
     */
1104
    protected function _standardizePath($path)
1105
    {
1106
        return preg_replace('/\\\\+/', '/', $path);
1107
    }
1108
  
1109
    /**
1110
     * Escape trace path for windows systems
1111
     *
1112
     * @param array $trace
1113
     * @return array
1114
     */
1115
    protected function _escapeTrace($trace)
1116
    {
1117
        if (!$trace) return $trace;
0 ignored issues
show
Bug Best Practice introduced by
The expression $trace of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1118
        for ($i = 0; $i < sizeof($trace); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function sizeof() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
1119
            if (isset($trace[$i]['file'])) {
1120
                $trace[$i]['file'] = $this->_escapeTraceFile($trace[$i]['file']);
1121
            }
1122
            if (isset($trace[$i]['args'])) {
1123
                $trace[$i]['args'] = $this->encodeObject($trace[$i]['args']);
1124
            }
1125
        }
1126
        return $trace;    
1127
    }
1128
  
1129
    /**
1130
     * Escape file information of trace for windows systems
1131
     *
1132
     * @param string $file
1133
     * @return string
1134
     */
1135
    protected function _escapeTraceFile($file)
1136
    {
1137
        /* Check if we have a windows filepath */
1138
        if (strpos($file, '\\')) {
1139
            /* First strip down to single \ */
1140
1141
            $file = preg_replace('/\\\\+/', '\\', $file);
1142
1143
            return $file;
1144
        }
1145
        return $file;
1146
    }
1147
1148
    /**
1149
     * Check if headers have already been sent
1150
     *
1151
     * @param string $filename
1152
     * @param integer $linenum
1153
     */
1154
    protected function headersSent(&$filename, &$linenum)
1155
    {
1156
        return headers_sent($filename, $linenum);
1157
    }
1158
1159
    /**
1160
     * Send header
1161
     *
1162
     * @param string $name
1163
     * @param string $value
1164
     */
1165
    protected function setHeader($name, $value)
1166
    {
1167
        return header($name . ': ' . $value);
1168
    }
1169
1170
    /**
1171
     * Get user agent
1172
     *
1173
     * @return string|false
1174
     */
1175
    protected function getUserAgent()
1176
    {
1177
        if (!isset($_SERVER['HTTP_USER_AGENT'])) return false;
1178
        return $_SERVER['HTTP_USER_AGENT'];
1179
    }
1180
1181
    /**
1182
     * Get all request headers
1183
     * 
1184
     * @return array
1185
     */
1186
    public static function getAllRequestHeaders()
1187
    {
1188
        static $_cachedHeaders = false;
1189
        if ($_cachedHeaders !== false) {
1190
            return $_cachedHeaders;
1191
        }
1192
        $headers = array();
1193
        if (function_exists('getallheaders')) {
1194
            foreach (getallheaders() as $name => $value) {
1195
                $headers[strtolower($name)] = $value;
1196
            }
1197
        } else {
1198
            foreach ($_SERVER as $name => $value) {
1199
                if (substr($name, 0, 5) == 'HTTP_') {
1200
                    $headers[strtolower(str_replace(' ', '-', str_replace('_', ' ', substr($name, 5))))] = $value;
1201
                }
1202
            }
1203
        }
1204
        return $_cachedHeaders = $headers;
1205
    }
1206
1207
    /**
1208
     * Get a request header
1209
     *
1210
     * @return string|false
1211
     */
1212
    protected function getRequestHeader($name)
1213
    {
1214
        $headers = self::getAllRequestHeaders();
1215
        if (isset($headers[strtolower($name)])) {
1216
            return $headers[strtolower($name)];
1217
        }
1218
        return false;
1219
    }
1220
1221
    /**
1222
     * Returns a new exception
1223
     *
1224
     * @param string $message
1225
     * @return Exception
1226
     */
1227
    protected function newException($message)
1228
    {
1229
        return new Exception($message);
1230
    }
1231
  
1232
    /**
1233
     * Encode an object into a JSON string
1234
     * 
1235
     * Uses PHP's jeson_encode() if available
1236
     * 
1237
     * @param object $object The object to be encoded
1238
     * @param boolean $skipObjectEncode
1239
     * @return string The JSON string
1240
     */
1241
    public function jsonEncode($object, $skipObjectEncode = false)
1242
    {
1243
        if (!$skipObjectEncode) {
1244
            $object = $this->encodeObject($object);
1245
        }
1246
        
1247
        if (function_exists('json_encode')
1248
           && $this->options['useNativeJsonEncode'] != false) {
1249
    
1250
            return json_encode($object);
1251
        } else {
1252
            return $this->json_encode($object);
1253
        }
1254
    }
1255
1256
    /**
1257
     * Encodes a table by encoding each row and column with encodeObject()
1258
     * 
1259
     * @param array $table The table to be encoded
1260
     * @return array
1261
     */  
1262
    protected function encodeTable($table)
1263
    {
1264
        if (!$table) return $table;
0 ignored issues
show
Bug Best Practice introduced by
The expression $table of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1265
        
1266
        $newTable = array();
1267
        foreach ($table as $row) {
1268
1269
            if (is_array($row)) {
1270
                $newRow = array();
1271
1272
                foreach ($row as $item) {
1273
                    $newRow[] = $this->encodeObject($item);
1274
                }
1275
1276
                $newTable[] = $newRow;
1277
            }
1278
        }
1279
1280
        return $newTable;
1281
    }
1282
1283
    /**
1284
     * Encodes an object including members with
1285
     * protected and private visibility
1286
     * 
1287
     * @param object $object The object to be encoded
1288
     * @param integer $Depth The current traversal depth
0 ignored issues
show
Bug introduced by
There is no parameter named $Depth. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1289
     * @return array All members of the object
1290
     */
1291
    protected function encodeObject($object, $objectDepth = 1, $arrayDepth = 1, $maxDepth = 1)
1292
    {
1293
        if ($maxDepth > $this->options['maxDepth']) {
1294
            return '** Max Depth (' . $this->options['maxDepth'] . ') **';
1295
        }
1296
1297
        $return = array();
1298
    
1299
        //#2801 is_resource reports false for closed resources https://bugs.php.net/bug.php?id=28016
1300
        if (is_resource($object) || gettype($object) === "unknown type") {
1301
    
1302
            return '** ' . (string) $object . ' **';
1303
    
1304
        } else if (is_object($object)) {
1305
    
1306
            if ($objectDepth > $this->options['maxObjectDepth']) {
1307
                return '** Max Object Depth (' . $this->options['maxObjectDepth'] . ') **';
1308
            }
1309
            
1310
            foreach ($this->objectStack as $refVal) {
1311
                if ($refVal === $object) {
1312
                    return '** Recursion (' . get_class($object) . ') **';
1313
                }
1314
            }
1315
            array_push($this->objectStack, $object);
1316
                    
1317
            $return['__className'] = $class = get_class($object);
1318
            $classLower = strtolower($class);
1319
1320
            $reflectionClass = new ReflectionClass($class);
1321
            $properties = array();
1322
            foreach ($reflectionClass->getProperties() as $property) {
1323
                $properties[$property->getName()] = $property;
1324
            }
1325
                
1326
            $members = (array)$object;
1327
    
1328
            foreach ($properties as $plainName => $property) {
1329
    
1330
                $name = $rawName = $plainName;
1331
                if ($property->isStatic()) {
1332
                    $name = 'static:' . $name;
1333
                }
1334
                if ($property->isPublic()) {
1335
                    $name = 'public:' . $name;
1336
                } else if ($property->isPrivate()) {
1337
                    $name = 'private:' . $name;
1338
                    $rawName = "\0" . $class . "\0" . $rawName;
1339
                } else if ($property->isProtected()) {
1340
                    $name = 'protected:' . $name;
1341
                    $rawName = "\0" . '*' . "\0" . $rawName;
1342
                }
1343
    
1344
                if (!(isset($this->objectFilters[$classLower])
1345
                     && is_array($this->objectFilters[$classLower])
1346
                     && in_array($plainName, $this->objectFilters[$classLower]))) {
1347
    
1348
                    if (array_key_exists($rawName, $members) && !$property->isStatic()) {
1349
                        $return[$name] = $this->encodeObject($members[$rawName], $objectDepth + 1, 1, $maxDepth + 1);
1350
                    } else {
1351
                        if (method_exists($property, 'setAccessible')) {
1352
                            $property->setAccessible(true);
1353
                            $return[$name] = $this->encodeObject($property->getValue($object), $objectDepth + 1, 1, $maxDepth + 1);
1354
                        } else
1355
                        if ($property->isPublic()) {
1356
                            $return[$name] = $this->encodeObject($property->getValue($object), $objectDepth + 1, 1, $maxDepth + 1);
1357
                        } else {
1358
                            $return[$name] = '** Need PHP 5.3 to get value **';
1359
                        }
1360
                    }
1361
                } else {
1362
                    $return[$name] = '** Excluded by Filter **';
1363
                }
1364
            }
1365
            
1366
            // Include all members that are not defined in the class
1367
            // but exist in the object
1368
            foreach ($members as $rawName => $value) {
1369
    
1370
                $name = $rawName;
1371
1372
                if ($name{0} == "\0") {
1373
                    $parts = explode("\0", $name);
1374
                    $name = $parts[2];
1375
                }
1376
1377
                $plainName = $name;
1378
    
1379
                if (!isset($properties[$name])) {
1380
                    $name = 'undeclared:' . $name;
1381
    
1382
                    if (!(isset($this->objectFilters[$classLower])
1383
                         && is_array($this->objectFilters[$classLower])
1384
                         && in_array($plainName, $this->objectFilters[$classLower]))) {
1385
    
1386
                        $return[$name] = $this->encodeObject($value, $objectDepth + 1, 1, $maxDepth + 1);
1387
                    } else {
1388
                        $return[$name] = '** Excluded by Filter **';
1389
                    }
1390
                }
1391
            }
1392
            
1393
            array_pop($this->objectStack);
1394
            
1395
        } elseif (is_array($object)) {
1396
    
1397
            if ($arrayDepth > $this->options['maxArrayDepth']) {
1398
                return '** Max Array Depth (' . $this->options['maxArrayDepth'] . ') **';
1399
            }
1400
          
1401
            foreach ($object as $key => $val) {                
1402
1403
                // Encoding the $GLOBALS PHP array causes an infinite loop
1404
                // if the recursion is not reset here as it contains
1405
                // a reference to itself. This is the only way I have come up
1406
                // with to stop infinite recursion in this case.
1407 View Code Duplication
                if ($key == 'GLOBALS'
1408
                   && is_array($val)
1409
                   && array_key_exists('GLOBALS', $val)) {
1410
                    $val['GLOBALS'] = '** Recursion (GLOBALS) **';
1411
                }
1412
1413
                if (!$this->is_utf8($key)) {
1414
                    $key = utf8_encode($key);
1415
                }
1416
1417
                $return[$key] = $this->encodeObject($val, 1, $arrayDepth + 1, $maxDepth + 1);
1418
            }
1419
		} elseif ( is_bool($object) ) {
1420
			return $object;
1421
		} elseif ( is_null($object) ) {
1422
			return $object;
1423
		} elseif ( is_numeric($object) ) {
1424
			return $object;
1425
		} else {
1426
            if ($this->is_utf8($object)) {
1427
                return $object;
1428
            } else {
1429
                return utf8_encode($object);
1430
            }
1431
        }
1432
        return $return;
1433
    }
1434
1435
    /**
1436
     * Returns true if $string is valid UTF-8 and false otherwise.
1437
     *
1438
     * @param mixed $str String to be tested
1439
     * @return boolean
1440
     */
1441
    protected function is_utf8($str)
1442
    {
1443
        if (function_exists('mb_detect_encoding')) {
1444
            return (
1445
                mb_detect_encoding($str, 'UTF-8', true) == 'UTF-8' &&
1446
                ($str === null || $this->jsonEncode($str, true) !== 'null')
1447
            );
1448
        }
1449
        $c = 0;
1450
        $b = 0;
1451
        $bits = 0;
1452
        $len = strlen($str);
1453
        for ($i = 0; $i < $len; $i++) {
1454
            $c = ord($str[$i]);
1455
            if ($c > 128) {
1456
                if (($c >= 254)) return false;
1457
                elseif ($c >= 252) $bits = 6;
1458
                elseif ($c >= 248) $bits = 5;
1459
                elseif ($c >= 240) $bits = 4;
1460
                elseif ($c >= 224) $bits = 3;
1461
                elseif ($c >= 192) $bits = 2;
1462
                else return false;
1463
                if (($i + $bits) > $len) return false;
1464
                while($bits > 1) {
1465
                    $i++;
1466
                    $b = ord($str[$i]);
1467
                    if ($b < 128 || $b > 191) return false;
1468
                    $bits--;
1469
                }
1470
            }
1471
        }
1472
        return ($str === null || $this->jsonEncode($str, true) !== 'null');
1473
    } 
1474
1475
    /**
1476
     * Converts to and from JSON format.
1477
     *
1478
     * JSON (JavaScript Object Notation) is a lightweight data-interchange
1479
     * format. It is easy for humans to read and write. It is easy for machines
1480
     * to parse and generate. It is based on a subset of the JavaScript
1481
     * Programming Language, Standard ECMA-262 3rd Edition - December 1999.
1482
     * This feature can also be found in  Python. JSON is a text format that is
1483
     * completely language independent but uses conventions that are familiar
1484
     * to programmers of the C-family of languages, including C, C++, C#, Java,
1485
     * JavaScript, Perl, TCL, and many others. These properties make JSON an
1486
     * ideal data-interchange language.
1487
     *
1488
     * This package provides a simple encoder and decoder for JSON notation. It
1489
     * is intended for use with client-side Javascript applications that make
1490
     * use of HTTPRequest to perform server communication functions - data can
1491
     * be encoded into JSON notation for use in a client-side javascript, or
1492
     * decoded from incoming Javascript requests. JSON format is native to
1493
     * Javascript, and can be directly eval()'ed with no further parsing
1494
     * overhead
1495
     *
1496
     * All strings should be in ASCII or UTF-8 format!
1497
     *
1498
     * LICENSE: Redistribution and use in source and binary forms, with or
1499
     * without modification, are permitted provided that the following
1500
     * conditions are met: Redistributions of source code must retain the
1501
     * above copyright notice, this list of conditions and the following
1502
     * disclaimer. Redistributions in binary form must reproduce the above
1503
     * copyright notice, this list of conditions and the following disclaimer
1504
     * in the documentation and/or other materials provided with the
1505
     * distribution.
1506
     *
1507
     * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
1508
     * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
1509
     * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
1510
     * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
1511
     * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
1512
     * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
1513
     * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
1514
     * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
1515
     * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
1516
     * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
1517
     * DAMAGE.
1518
     *
1519
     * @category
1520
     * @package     Services_JSON
1521
     * @author      Michal Migurski <[email protected]>
1522
     * @author      Matt Knapp <mdknapp[at]gmail[dot]com>
1523
     * @author      Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
1524
     * @author      Christoph Dorn <[email protected]>
1525
     * @copyright   2005 Michal Migurski
1526
     * @version     CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $
1527
     * @license     http://www.opensource.org/licenses/bsd-license.php
1528
     * @link        http://pear.php.net/pepr/pepr-proposal-show.php?id=198
1529
     */
1530
   
1531
     
1532
    /**
1533
     * Keep a list of objects as we descend into the array so we can detect recursion.
1534
     */
1535
    private $json_objectStack = array();
1536
1537
1538
   /**
1539
    * convert a string from one UTF-8 char to one UTF-16 char
1540
    *
1541
    * Normally should be handled by mb_convert_encoding, but
1542
    * provides a slower PHP-only method for installations
1543
    * that lack the multibye string extension.
1544
    *
1545
    * @param    string  $utf8   UTF-8 character
1546
    * @return   string  UTF-16 character
1547
    * @access   private
1548
    */
1549
    private function json_utf82utf16($utf8)
1550
    {
1551
        // oh please oh please oh please oh please oh please
1552
        if (function_exists('mb_convert_encoding')) {
1553
            return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
1554
        }
1555
1556
        switch (strlen($utf8)) {
1557
            case 1:
1558
                // this case should never be reached, because we are in ASCII range
1559
                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1560
                return $utf8;
1561
1562
            case 2:
1563
                // return a UTF-16 character from a 2-byte UTF-8 char
1564
                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1565
                return chr(0x07 & (ord($utf8{0}) >> 2))
1566
                       . chr((0xC0 & (ord($utf8{0}) << 6))
1567
                       | (0x3F & ord($utf8{1})));
1568
1569
            case 3:
1570
                // return a UTF-16 character from a 3-byte UTF-8 char
1571
                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1572
                return chr((0xF0 & (ord($utf8{0}) << 4))
1573
                       | (0x0F & (ord($utf8{1}) >> 2)))
1574
                       . chr((0xC0 & (ord($utf8{1}) << 6))
1575
                       | (0x7F & ord($utf8{2})));
1576
        }
1577
1578
        // ignoring UTF-32 for now, sorry
1579
        return '';
1580
    }
1581
1582
   /**
1583
    * encodes an arbitrary variable into JSON format
1584
    *
1585
    * @param    mixed   $var    any number, boolean, string, array, or object to be encoded.
1586
    *                           see argument 1 to Services_JSON() above for array-parsing behavior.
1587
    *                           if var is a strng, note that encode() always expects it
1588
    *                           to be in ASCII or UTF-8 format!
1589
    *
1590
    * @return   mixed   JSON string representation of input var or an error if a problem occurs
1591
    * @access   public
1592
    */
1593
    private function json_encode($var)
1594
    {
1595
        if (is_object($var)) {
1596
            if (in_array($var, $this->json_objectStack)) {
1597
                return '"** Recursion **"';
1598
            }
1599
        }
1600
          
1601
        switch (gettype($var)) {
1602
            case 'boolean':
1603
                return $var ? 'true' : 'false';
1604
1605
            case 'NULL':
1606
                return 'null';
1607
1608
            case 'integer':
1609
                return (int) $var;
1610
1611
            case 'double':
1612
            case 'float':
1613
                return (float) $var;
1614
1615
            case 'string':
1616
                // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
1617
                $ascii = '';
1618
                $strlen_var = strlen($var);
1619
1620
               /*
1621
                * Iterate over every character in the string,
1622
                * escaping with a slash or encoding to UTF-8 where necessary
1623
                */
1624
                for ($c = 0; $c < $strlen_var; ++$c) {
1625
1626
                    $ord_var_c = ord($var{$c});
1627
1628
                    switch (true) {
1629
                        case $ord_var_c == 0x08:
1630
                            $ascii .= '\b';
1631
                            break;
1632
                        case $ord_var_c == 0x09:
1633
                            $ascii .= '\t';
1634
                            break;
1635
                        case $ord_var_c == 0x0A:
1636
                            $ascii .= '\n';
1637
                            break;
1638
                        case $ord_var_c == 0x0C:
1639
                            $ascii .= '\f';
1640
                            break;
1641
                        case $ord_var_c == 0x0D:
1642
                            $ascii .= '\r';
1643
                            break;
1644
1645
                        case $ord_var_c == 0x22:
1646
                        case $ord_var_c == 0x2F:
1647
                        case $ord_var_c == 0x5C:
1648
                            // double quote, slash, slosh
1649
                            $ascii .= '\\' . $var{$c};
1650
                            break;
1651
1652
                        case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
1653
                            // characters U-00000000 - U-0000007F (same as ASCII)
1654
                            $ascii .= $var{$c};
1655
                            break;
1656
1657
                        case (($ord_var_c & 0xE0) == 0xC0):
1658
                            // characters U-00000080 - U-000007FF, mask 110XXXXX
1659
                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1660
                            $char = pack('C*', $ord_var_c, ord($var{$c + 1}));
1661
                            $c += 1;
1662
                            $utf16 = $this->json_utf82utf16($char);
1663
                            $ascii .= sprintf('\u%04s', bin2hex($utf16));
1664
                            break;
1665
1666
                        case (($ord_var_c & 0xF0) == 0xE0):
1667
                            // characters U-00000800 - U-0000FFFF, mask 1110XXXX
1668
                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1669
                            $char = pack('C*', $ord_var_c,
1670
                                         ord($var{$c + 1}),
1671
                                         ord($var{$c + 2}));
1672
                            $c += 2;
1673
                            $utf16 = $this->json_utf82utf16($char);
1674
                            $ascii .= sprintf('\u%04s', bin2hex($utf16));
1675
                            break;
1676
1677
                        case (($ord_var_c & 0xF8) == 0xF0):
1678
                            // characters U-00010000 - U-001FFFFF, mask 11110XXX
1679
                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1680
                            $char = pack('C*', $ord_var_c,
1681
                                         ord($var{$c + 1}),
1682
                                         ord($var{$c + 2}),
1683
                                         ord($var{$c + 3}));
1684
                            $c += 3;
1685
                            $utf16 = $this->json_utf82utf16($char);
1686
                            $ascii .= sprintf('\u%04s', bin2hex($utf16));
1687
                            break;
1688
1689 View Code Duplication
                        case (($ord_var_c & 0xFC) == 0xF8):
1690
                            // characters U-00200000 - U-03FFFFFF, mask 111110XX
1691
                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1692
                            $char = pack('C*', $ord_var_c,
1693
                                         ord($var{$c + 1}),
1694
                                         ord($var{$c + 2}),
1695
                                         ord($var{$c + 3}),
1696
                                         ord($var{$c + 4}));
1697
                            $c += 4;
1698
                            $utf16 = $this->json_utf82utf16($char);
1699
                            $ascii .= sprintf('\u%04s', bin2hex($utf16));
1700
                            break;
1701
1702 View Code Duplication
                        case (($ord_var_c & 0xFE) == 0xFC):
1703
                            // characters U-04000000 - U-7FFFFFFF, mask 1111110X
1704
                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1705
                            $char = pack('C*', $ord_var_c,
1706
                                         ord($var{$c + 1}),
1707
                                         ord($var{$c + 2}),
1708
                                         ord($var{$c + 3}),
1709
                                         ord($var{$c + 4}),
1710
                                         ord($var{$c + 5}));
1711
                            $c += 5;
1712
                            $utf16 = $this->json_utf82utf16($char);
1713
                            $ascii .= sprintf('\u%04s', bin2hex($utf16));
1714
                            break;
1715
                    }
1716
                }
1717
1718
                return '"' . $ascii . '"';
1719
1720
            case 'array':
1721
                /*
1722
                 * As per JSON spec if any array key is not an integer
1723
                 * we must treat the the whole array as an object. We
1724
                 * also try to catch a sparsely populated associative
1725
                 * array with numeric keys here because some JS engines
1726
                 * will create an array with empty indexes up to
1727
                 * max_index which can cause memory issues and because
1728
                 * the keys, which may be relevant, will be remapped
1729
                 * otherwise.
1730
                 *
1731
                 * As per the ECMA and JSON specification an object may
1732
                 * have any string as a property. Unfortunately due to
1733
                 * a hole in the ECMA specification if the key is a
1734
                 * ECMA reserved word or starts with a digit the
1735
                 * parameter is only accessible using ECMAScript's
1736
                 * bracket notation.
1737
                 */
1738
1739
                // treat as a JSON object
1740
                if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
1741
                  
1742
                    $this->json_objectStack[] = $var;
1743
1744
                    $properties = array_map(array($this, 'json_name_value'),
1745
                                            array_keys($var),
1746
                                            array_values($var));
1747
1748
                    array_pop($this->json_objectStack);
1749
1750
                    foreach ($properties as $property) {
1751
                        if ($property instanceof Exception) {
1752
                            return $property;
1753
                        }
1754
                    }
1755
1756
                    return '{' . join(',', $properties) . '}';
1757
                }
1758
1759
                $this->json_objectStack[] = $var;
1760
1761
                // treat it like a regular array
1762
                $elements = array_map(array($this, 'json_encode'), $var);
1763
1764
                array_pop($this->json_objectStack);
1765
1766
                foreach ($elements as $element) {
1767
                    if ($element instanceof Exception) {
1768
                        return $element;
1769
                    }
1770
                }
1771
1772
                return '[' . join(',', $elements) . ']';
1773
1774
            case 'object':
1775
                $vars = self::encodeObject($var);
1776
1777
                $this->json_objectStack[] = $var;
1778
1779
                $properties = array_map(array($this, 'json_name_value'),
1780
                                        array_keys($vars),
1781
                                        array_values($vars));
1782
1783
                array_pop($this->json_objectStack);
1784
              
1785
                foreach ($properties as $property) {
1786
                    if ($property instanceof Exception) {
1787
                        return $property;
1788
                    }
1789
                }
1790
                     
1791
                return '{' . join(',', $properties) . '}';
1792
1793
            default:
1794
                return null;
1795
        }
1796
    }
1797
1798
   /**
1799
    * array-walking function for use in generating JSON-formatted name-value pairs
1800
    *
1801
    * @param    string  $name   name of key to use
1802
    * @param    mixed   $value  reference to an array element to be encoded
1803
    *
1804
    * @return   string  JSON-formatted name-value pair, like '"name":value'
1805
    * @access   private
1806
    */
1807
    private function json_name_value($name, $value)
1808
    {
1809
        // Encoding the $GLOBALS PHP array causes an infinite loop
1810
        // if the recursion is not reset here as it contains
1811
        // a reference to itself. This is the only way I have come up
1812
        // with to stop infinite recursion in this case.
1813 View Code Duplication
        if ($name == 'GLOBALS'
1814
           && is_array($value)
1815
           && array_key_exists('GLOBALS', $value)) {
1816
            $value['GLOBALS'] = '** Recursion **';
1817
        }
1818
    
1819
        $encodedValue = $this->json_encode($value);
1820
1821
        if ($encodedValue instanceof Exception) {
1822
            return $encodedValue;
1823
        }
1824
1825
        return $this->json_encode(strval($name)) . ':' . $encodedValue;
1826
    }
1827
1828
    /**
1829
     * @deprecated
1830
     */
1831
    public function setProcessorUrl($URL)
1832
    {
1833
        trigger_error('The FirePHP::setProcessorUrl() method is no longer supported', E_USER_DEPRECATED);
1834
    }
1835
1836
    /**
1837
     * @deprecated
1838
     */
1839
    public function setRendererUrl($URL)
1840
    {
1841
        trigger_error('The FirePHP::setRendererUrl() method is no longer supported', E_USER_DEPRECATED);
1842
    }
1843
}