Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/lib/minify/lib/FirePHP.php
Go to the documentation of this file.
00001 <?php
00058 class FirePHP {
00059   
00065   const VERSION = '0.2.0';
00066   
00074   const LOG = 'LOG';
00075   
00083   const INFO = 'INFO';
00084   
00092   const WARN = 'WARN';
00093   
00101   const ERROR = 'ERROR';
00102   
00108   const DUMP = 'DUMP';
00109   
00115   const TRACE = 'TRACE';
00116   
00124   const EXCEPTION = 'EXCEPTION';
00125   
00131   const TABLE = 'TABLE';
00132   
00138   const GROUP_START = 'GROUP_START';
00139   
00145   const GROUP_END = 'GROUP_END';
00146   
00152   protected static $instance = null;
00153   
00159   protected $messageIndex = 1;
00160     
00166   protected $options = array();
00167   
00173   protected $objectFilters = array();
00174   
00180   protected $objectStack = array();
00181   
00187   protected $enabled = true;
00188   
00192   function __construct() {
00193     $this->options['maxObjectDepth'] = 10;
00194     $this->options['maxArrayDepth'] = 20;
00195     $this->options['useNativeJsonEncode'] = true;
00196     $this->options['includeLineNumbers'] = true;
00197   }
00198     
00204   public function __sleep() {
00205     return array('options','objectFilters','enabled');
00206   }
00207     
00214   public static function getInstance($AutoCreate=false) {
00215     if($AutoCreate===true && !self::$instance) {
00216       self::init();
00217     }
00218     return self::$instance;
00219   }
00220    
00226   public static function init() {
00227     return self::$instance = new self();
00228   }
00229   
00236   public function setEnabled($Enabled) {
00237     $this->enabled = $Enabled;
00238   }
00239   
00245   public function getEnabled() {
00246     return $this->enabled;
00247   }
00248   
00258   public function setObjectFilter($Class, $Filter) {
00259     $this->objectFilters[$Class] = $Filter;
00260   }
00261   
00274   public function setOptions($Options) {
00275     $this->options = array_merge($this->options,$Options);
00276   }
00277   
00283   public function registerErrorHandler()
00284   {
00285     //NOTE: The following errors will not be caught by this error handler:
00286     //      E_ERROR, E_PARSE, E_CORE_ERROR,
00287     //      E_CORE_WARNING, E_COMPILE_ERROR,
00288     //      E_COMPILE_WARNING, E_STRICT
00289     
00290     set_error_handler(array($this,'errorHandler'));     
00291   }
00292 
00304   public function errorHandler($errno, $errstr, $errfile, $errline, $errcontext)
00305   {
00306     // Don't throw exception if error reporting is switched off
00307     if (error_reporting() == 0) {
00308       return;
00309     }
00310     // Only throw exceptions for errors we are asking for
00311     if (error_reporting() & $errno) {
00312       throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
00313     }
00314   }
00315   
00319   public function registerExceptionHandler()
00320   {
00321     set_exception_handler(array($this,'exceptionHandler'));     
00322   }
00323   
00332   function exceptionHandler($Exception) {
00333     $this->fb($Exception);
00334   }
00335   
00341   public function setProcessorUrl($URL)
00342   {
00343     $this->setHeader('X-FirePHP-ProcessorURL', $URL);
00344   }
00345 
00351   public function setRendererUrl($URL)
00352   {
00353     $this->setHeader('X-FirePHP-RendererURL', $URL);
00354   }
00355   
00363   public function group($Name) {
00364     return $this->fb(null, $Name, FirePHP::GROUP_START);
00365   }
00366   
00373   public function groupEnd() {
00374     return $this->fb(null, null, FirePHP::GROUP_END);
00375   }
00376 
00386   public function log($Object, $Label=null) {
00387     return $this->fb($Object, $Label, FirePHP::LOG);
00388   } 
00389 
00399   public function info($Object, $Label=null) {
00400     return $this->fb($Object, $Label, FirePHP::INFO);
00401   } 
00402 
00412   public function warn($Object, $Label=null) {
00413     return $this->fb($Object, $Label, FirePHP::WARN);
00414   } 
00415 
00425   public function error($Object, $Label=null) {
00426     return $this->fb($Object, $Label, FirePHP::ERROR);
00427   } 
00428 
00438   public function dump($Key, $Variable) {
00439     return $this->fb($Variable, $Key, FirePHP::DUMP);
00440   }
00441   
00450   public function trace($Label) {
00451     return $this->fb($Label, FirePHP::TRACE);
00452   } 
00453 
00463   public function table($Label, $Table) {
00464     return $this->fb($Table, $Label, FirePHP::TABLE);
00465   }
00466   
00472   public function detectClientExtension() {
00473     /* Check if FirePHP is installed on client */
00474     if(!@preg_match_all('/\sFirePHP\/([\.|\d]*)\s?/si',$this->getUserAgent(),$m) ||
00475        !version_compare($m[1][0],'0.0.6','>=')) {
00476       return false;
00477     }
00478     return true;    
00479   }
00480  
00489   public function fb($Object) {
00490   
00491     if(!$this->enabled) {
00492       return false;
00493     }
00494   
00495     if (headers_sent($filename, $linenum)) {
00496         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.');
00497     }
00498   
00499     $Type = null;
00500     $Label = null;
00501   
00502     if(func_num_args()==1) {
00503     } else
00504     if(func_num_args()==2) {
00505       switch(func_get_arg(1)) {
00506         case self::LOG:
00507         case self::INFO:
00508         case self::WARN:
00509         case self::ERROR:
00510         case self::DUMP:
00511         case self::TRACE:
00512         case self::EXCEPTION:
00513         case self::TABLE:
00514         case self::GROUP_START:
00515         case self::GROUP_END:
00516           $Type = func_get_arg(1);
00517           break;
00518         default:
00519           $Label = func_get_arg(1);
00520           break;
00521       }
00522     } else
00523     if(func_num_args()==3) {
00524       $Type = func_get_arg(2);
00525       $Label = func_get_arg(1);
00526     } else {
00527       throw $this->newException('Wrong number of arguments to fb() function!');
00528     }
00529   
00530   
00531     if(!$this->detectClientExtension()) {
00532       return false;
00533     }
00534   
00535     $meta = array();
00536     $skipFinalObjectEncode = false;
00537   
00538     if($Object instanceof Exception) {
00539 
00540       $meta['file'] = $this->_escapeTraceFile($Object->getFile());
00541       $meta['line'] = $Object->getLine();
00542       
00543       $trace = $Object->getTrace();
00544       if($Object instanceof ErrorException
00545          && isset($trace[0]['function'])
00546          && $trace[0]['function']=='errorHandler'
00547          && isset($trace[0]['class'])
00548          && $trace[0]['class']=='FirePHP') {
00549            
00550         $severity = false;
00551         switch($Object->getSeverity()) {
00552           case E_WARNING: $severity = 'E_WARNING'; break;
00553           case E_NOTICE: $severity = 'E_NOTICE'; break;
00554           case E_USER_ERROR: $severity = 'E_USER_ERROR'; break;
00555           case E_USER_WARNING: $severity = 'E_USER_WARNING'; break;
00556           case E_USER_NOTICE: $severity = 'E_USER_NOTICE'; break;
00557           case E_STRICT: $severity = 'E_STRICT'; break;
00558           case E_RECOVERABLE_ERROR: $severity = 'E_RECOVERABLE_ERROR'; break;
00559           case E_DEPRECATED: $severity = 'E_DEPRECATED'; break;
00560           case E_USER_DEPRECATED: $severity = 'E_USER_DEPRECATED'; break;
00561         }
00562            
00563         $Object = array('Class'=>get_class($Object),
00564                         'Message'=>$severity.': '.$Object->getMessage(),
00565                         'File'=>$this->_escapeTraceFile($Object->getFile()),
00566                         'Line'=>$Object->getLine(),
00567                         'Type'=>'trigger',
00568                         'Trace'=>$this->_escapeTrace(array_splice($trace,2)));
00569         $skipFinalObjectEncode = true;
00570       } else {
00571         $Object = array('Class'=>get_class($Object),
00572                         'Message'=>$Object->getMessage(),
00573                         'File'=>$this->_escapeTraceFile($Object->getFile()),
00574                         'Line'=>$Object->getLine(),
00575                         'Type'=>'throw',
00576                         'Trace'=>$this->_escapeTrace($trace));
00577         $skipFinalObjectEncode = true;
00578       }
00579       $Type = self::EXCEPTION;
00580       
00581     } else
00582     if($Type==self::TRACE) {
00583       
00584       $trace = debug_backtrace();
00585       if(!$trace) return false;
00586       for( $i=0 ; $i<sizeof($trace) ; $i++ ) {
00587 
00588         if(isset($trace[$i]['class'])
00589            && isset($trace[$i]['file'])
00590            && ($trace[$i]['class']=='FirePHP'
00591                || $trace[$i]['class']=='FB')
00592            && (substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php'
00593                || substr($this->_standardizePath($trace[$i]['file']),-29,29)=='FirePHPCore/FirePHP.class.php')) {
00594           /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */
00595         } else
00596         if(isset($trace[$i]['class'])
00597            && isset($trace[$i+1]['file'])
00598            && $trace[$i]['class']=='FirePHP'
00599            && substr($this->_standardizePath($trace[$i+1]['file']),-18,18)=='FirePHPCore/fb.php') {
00600           /* Skip fb() */
00601         } else
00602         if($trace[$i]['function']=='fb'
00603            || $trace[$i]['function']=='trace'
00604            || $trace[$i]['function']=='send') {
00605           $Object = array('Class'=>isset($trace[$i]['class'])?$trace[$i]['class']:'',
00606                           'Type'=>isset($trace[$i]['type'])?$trace[$i]['type']:'',
00607                           'Function'=>isset($trace[$i]['function'])?$trace[$i]['function']:'',
00608                           'Message'=>$trace[$i]['args'][0],
00609                           'File'=>isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):'',
00610                           'Line'=>isset($trace[$i]['line'])?$trace[$i]['line']:'',
00611                           'Args'=>isset($trace[$i]['args'])?$this->encodeObject($trace[$i]['args']):'',
00612                           'Trace'=>$this->_escapeTrace(array_splice($trace,$i+1)));
00613 
00614           $skipFinalObjectEncode = true;
00615           $meta['file'] = isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):'';
00616           $meta['line'] = isset($trace[$i]['line'])?$trace[$i]['line']:'';
00617           break;
00618         }
00619       }
00620 
00621     } else
00622     if($Type==self::TABLE) {
00623       
00624       if(isset($Object[0]) && is_string($Object[0])) {
00625         $Object[1] = $this->encodeTable($Object[1]);
00626       } else {
00627         $Object = $this->encodeTable($Object);
00628       }
00629 
00630       $skipFinalObjectEncode = true;
00631       
00632     } else {
00633       if($Type===null) {
00634         $Type = self::LOG;
00635       }
00636     }
00637     
00638     if($this->options['includeLineNumbers']) {
00639       if(!isset($meta['file']) || !isset($meta['line'])) {
00640 
00641         $trace = debug_backtrace();
00642         for( $i=0 ; $trace && $i<sizeof($trace) ; $i++ ) {
00643   
00644           if(isset($trace[$i]['class'])
00645              && isset($trace[$i]['file'])
00646              && ($trace[$i]['class']=='FirePHP'
00647                  || $trace[$i]['class']=='FB')
00648              && (substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php'
00649                  || substr($this->_standardizePath($trace[$i]['file']),-29,29)=='FirePHPCore/FirePHP.class.php')) {
00650             /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */
00651           } else
00652           if(isset($trace[$i]['class'])
00653              && isset($trace[$i+1]['file'])
00654              && $trace[$i]['class']=='FirePHP'
00655              && substr($this->_standardizePath($trace[$i+1]['file']),-18,18)=='FirePHPCore/fb.php') {
00656             /* Skip fb() */
00657           } else
00658           if(isset($trace[$i]['file'])
00659              && substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php') {
00660             /* Skip FB::fb() */
00661           } else {
00662             $meta['file'] = isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):'';
00663             $meta['line'] = isset($trace[$i]['line'])?$trace[$i]['line']:'';
00664             break;
00665           }
00666         }      
00667       
00668       }
00669     } else {
00670       unset($meta['file']);
00671       unset($meta['line']);
00672     }
00673 
00674         $this->setHeader('X-Wf-Protocol-1','http://meta.wildfirehq.org/Protocol/JsonStream/0.2');
00675         $this->setHeader('X-Wf-1-Plugin-1','http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/'.self::VERSION);
00676  
00677     $structure_index = 1;
00678     if($Type==self::DUMP) {
00679       $structure_index = 2;
00680         $this->setHeader('X-Wf-1-Structure-2','http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1');
00681     } else {
00682         $this->setHeader('X-Wf-1-Structure-1','http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1');
00683     }
00684   
00685     if($Type==self::DUMP) {
00686         $msg = '{"'.$Label.'":'.$this->jsonEncode($Object, $skipFinalObjectEncode).'}';
00687     } else {
00688       $msg_meta = array('Type'=>$Type);
00689       if($Label!==null) {
00690         $msg_meta['Label'] = $Label;
00691       }
00692       if(isset($meta['file'])) {
00693         $msg_meta['File'] = $meta['file'];
00694       }
00695       if(isset($meta['line'])) {
00696         $msg_meta['Line'] = $meta['line'];
00697       }
00698         $msg = '['.$this->jsonEncode($msg_meta).','.$this->jsonEncode($Object, $skipFinalObjectEncode).']';
00699     }
00700     
00701     $parts = explode("\n",chunk_split($msg, 5000, "\n"));
00702 
00703     for( $i=0 ; $i<count($parts) ; $i++) {
00704         
00705         $part = $parts[$i];
00706         if ($part) {
00707             
00708             if(count($parts)>2) {
00709               // Message needs to be split into multiple parts
00710               $this->setHeader('X-Wf-1-'.$structure_index.'-'.'1-'.$this->messageIndex,
00711                                (($i==0)?strlen($msg):'')
00712                                . '|' . $part . '|'
00713                                . (($i<count($parts)-2)?'\\':''));
00714             } else {
00715               $this->setHeader('X-Wf-1-'.$structure_index.'-'.'1-'.$this->messageIndex,
00716                                strlen($part) . '|' . $part . '|');
00717             }
00718             
00719             $this->messageIndex++;
00720             
00721             if ($this->messageIndex > 99999) {
00722                 throw new Exception('Maximum number (99,999) of messages reached!');             
00723             }
00724         }
00725     }
00726 
00727         $this->setHeader('X-Wf-1-Index',$this->messageIndex-1);
00728 
00729     return true;
00730   }
00731   
00738   protected function _standardizePath($Path) {
00739     return preg_replace('/\\\\+/','/',$Path);    
00740   }
00741   
00748   protected function _escapeTrace($Trace) {
00749     if(!$Trace) return $Trace;
00750     for( $i=0 ; $i<sizeof($Trace) ; $i++ ) {
00751       if(isset($Trace[$i]['file'])) {
00752         $Trace[$i]['file'] = $this->_escapeTraceFile($Trace[$i]['file']);
00753       }
00754       if(isset($Trace[$i]['args'])) {
00755         $Trace[$i]['args'] = $this->encodeObject($Trace[$i]['args']);
00756       }
00757     }
00758     return $Trace;    
00759   }
00760   
00767   protected function _escapeTraceFile($File) {
00768     /* Check if we have a windows filepath */
00769     if(strpos($File,'\\')) {
00770       /* First strip down to single \ */
00771       
00772       $file = preg_replace('/\\\\+/','\\',$File);
00773       
00774       return $file;
00775     }
00776     return $File;
00777   }
00778 
00785   protected function setHeader($Name, $Value) {
00786     return header($Name.': '.$Value);
00787   }
00788 
00794   protected function getUserAgent() {
00795     if(!isset($_SERVER['HTTP_USER_AGENT'])) return false;
00796     return $_SERVER['HTTP_USER_AGENT'];
00797   }
00798 
00805   protected function newException($Message) {
00806     return new Exception($Message);
00807   }
00808   
00817   protected function jsonEncode($Object, $skipObjectEncode=false)
00818   {
00819     if(!$skipObjectEncode) {
00820       $Object = $this->encodeObject($Object);
00821     }
00822     
00823     if(function_exists('json_encode')
00824        && $this->options['useNativeJsonEncode']!=false) {
00825 
00826       return json_encode($Object);
00827     } else {
00828       return $this->json_encode($Object);
00829     }
00830   }
00831   
00838   protected function encodeTable($Table) {
00839     if(!$Table) return $Table;
00840     for( $i=0 ; $i<count($Table) ; $i++ ) {
00841       if(is_array($Table[$i])) {
00842         for( $j=0 ; $j<count($Table[$i]) ; $j++ ) {
00843           $Table[$i][$j] = $this->encodeObject($Table[$i][$j]);
00844         }
00845       }
00846     }
00847     return $Table;
00848   }
00849   
00858   protected function encodeObject($Object, $ObjectDepth = 1, $ArrayDepth = 1)
00859   {
00860     $return = array();
00861     
00862     if (is_object($Object)) {
00863 
00864         if ($ObjectDepth > $this->options['maxObjectDepth']) {
00865           return '** Max Object Depth ('.$this->options['maxObjectDepth'].') **';
00866         }
00867         
00868         foreach ($this->objectStack as $refVal) {
00869             if ($refVal === $Object) {
00870                 return '** Recursion ('.get_class($Object).') **';
00871             }
00872         }
00873         array_push($this->objectStack, $Object);
00874                 
00875         $return['__className'] = $class = get_class($Object);
00876 
00877         $reflectionClass = new ReflectionClass($class);  
00878         $properties = array();
00879         foreach( $reflectionClass->getProperties() as $property) {
00880           $properties[$property->getName()] = $property;
00881         }
00882             
00883         $members = (array)$Object;
00884             
00885         foreach( $properties as $raw_name => $property ) {
00886           
00887           $name = $raw_name;
00888           if($property->isStatic()) {
00889             $name = 'static:'.$name;
00890           }
00891           if($property->isPublic()) {
00892             $name = 'public:'.$name;
00893           } else
00894           if($property->isPrivate()) {
00895             $name = 'private:'.$name;
00896             $raw_name = "\0".$class."\0".$raw_name;
00897           } else
00898           if($property->isProtected()) {
00899             $name = 'protected:'.$name;
00900             $raw_name = "\0".'*'."\0".$raw_name;
00901           }
00902           
00903           if(!(isset($this->objectFilters[$class])
00904                && is_array($this->objectFilters[$class])
00905                && in_array($raw_name,$this->objectFilters[$class]))) {
00906 
00907             if(array_key_exists($raw_name,$members)
00908                && !$property->isStatic()) {
00909               
00910               $return[$name] = $this->encodeObject($members[$raw_name], $ObjectDepth + 1, 1);      
00911             
00912             } else {
00913               if(method_exists($property,'setAccessible')) {
00914                 $property->setAccessible(true);
00915                 $return[$name] = $this->encodeObject($property->getValue($Object), $ObjectDepth + 1, 1);
00916               } else
00917               if($property->isPublic()) {
00918                 $return[$name] = $this->encodeObject($property->getValue($Object), $ObjectDepth + 1, 1);
00919               } else {
00920                 $return[$name] = '** Need PHP 5.3 to get value **';
00921               }
00922             }
00923           } else {
00924             $return[$name] = '** Excluded by Filter **';
00925           }
00926         }
00927         
00928         // Include all members that are not defined in the class
00929         // but exist in the object
00930         foreach( $members as $raw_name => $value ) {
00931           
00932           $name = $raw_name;
00933           
00934           if ($name{0} == "\0") {
00935             $parts = explode("\0", $name);
00936             $name = $parts[2];
00937           }
00938           
00939           if(!isset($properties[$name])) {
00940             $name = 'undeclared:'.$name;
00941               
00942             if(!(isset($this->objectFilters[$class])
00943                  && is_array($this->objectFilters[$class])
00944                  && in_array($raw_name,$this->objectFilters[$class]))) {
00945               
00946               $return[$name] = $this->encodeObject($value, $ObjectDepth + 1, 1);
00947             } else {
00948               $return[$name] = '** Excluded by Filter **';
00949             }
00950           }
00951         }
00952         
00953         array_pop($this->objectStack);
00954         
00955     } elseif (is_array($Object)) {
00956 
00957         if ($ArrayDepth > $this->options['maxArrayDepth']) {
00958           return '** Max Array Depth ('.$this->options['maxArrayDepth'].') **';
00959         }
00960       
00961         foreach ($Object as $key => $val) {
00962           
00963           // Encoding the $GLOBALS PHP array causes an infinite loop
00964           // if the recursion is not reset here as it contains
00965           // a reference to itself. This is the only way I have come up
00966           // with to stop infinite recursion in this case.
00967           if($key=='GLOBALS'
00968              && is_array($val)
00969              && array_key_exists('GLOBALS',$val)) {
00970             $val['GLOBALS'] = '** Recursion (GLOBALS) **';
00971           }
00972           
00973           $return[$key] = $this->encodeObject($val, 1, $ArrayDepth + 1);
00974         }
00975     } else {
00976       if(self::is_utf8($Object)) {
00977         return $Object;
00978       } else {
00979         return utf8_encode($Object);
00980       }
00981     }
00982     return $return;
00983   }
00984 
00991   protected static function is_utf8($str) {
00992     $c=0; $b=0;
00993     $bits=0;
00994     $len=strlen($str);
00995     for($i=0; $i<$len; $i++){
00996         $c=ord($str[$i]);
00997         if($c > 128){
00998             if(($c >= 254)) return false;
00999             elseif($c >= 252) $bits=6;
01000             elseif($c >= 248) $bits=5;
01001             elseif($c >= 240) $bits=4;
01002             elseif($c >= 224) $bits=3;
01003             elseif($c >= 192) $bits=2;
01004             else return false;
01005             if(($i+$bits) > $len) return false;
01006             while($bits > 1){
01007                 $i++;
01008                 $b=ord($str[$i]);
01009                 if($b < 128 || $b > 191) return false;
01010                 $bits--;
01011             }
01012         }
01013     }
01014     return true;
01015   } 
01016 
01077   private $json_objectStack = array();
01078 
01079 
01091   private function json_utf82utf16($utf8)
01092   {
01093       // oh please oh please oh please oh please oh please
01094       if(function_exists('mb_convert_encoding')) {
01095           return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
01096       }
01097 
01098       switch(strlen($utf8)) {
01099           case 1:
01100               // this case should never be reached, because we are in ASCII range
01101               // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
01102               return $utf8;
01103 
01104           case 2:
01105               // return a UTF-16 character from a 2-byte UTF-8 char
01106               // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
01107               return chr(0x07 & (ord($utf8{0}) >> 2))
01108                    . chr((0xC0 & (ord($utf8{0}) << 6))
01109                        | (0x3F & ord($utf8{1})));
01110 
01111           case 3:
01112               // return a UTF-16 character from a 3-byte UTF-8 char
01113               // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
01114               return chr((0xF0 & (ord($utf8{0}) << 4))
01115                        | (0x0F & (ord($utf8{1}) >> 2)))
01116                    . chr((0xC0 & (ord($utf8{1}) << 6))
01117                        | (0x7F & ord($utf8{2})));
01118       }
01119 
01120       // ignoring UTF-32 for now, sorry
01121       return '';
01122   }
01123 
01135   private function json_encode($var)
01136   {
01137     
01138     if(is_object($var)) {
01139       if(in_array($var,$this->json_objectStack)) {
01140         return '"** Recursion **"';
01141       }
01142     }
01143           
01144       switch (gettype($var)) {
01145           case 'boolean':
01146               return $var ? 'true' : 'false';
01147 
01148           case 'NULL':
01149               return 'null';
01150 
01151           case 'integer':
01152               return (int) $var;
01153 
01154           case 'double':
01155           case 'float':
01156               return (float) $var;
01157 
01158           case 'string':
01159               // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
01160               $ascii = '';
01161               $strlen_var = strlen($var);
01162 
01163              /*
01164               * Iterate over every character in the string,
01165               * escaping with a slash or encoding to UTF-8 where necessary
01166               */
01167               for ($c = 0; $c < $strlen_var; ++$c) {
01168 
01169                   $ord_var_c = ord($var{$c});
01170 
01171                   switch (true) {
01172                       case $ord_var_c == 0x08:
01173                           $ascii .= '\b';
01174                           break;
01175                       case $ord_var_c == 0x09:
01176                           $ascii .= '\t';
01177                           break;
01178                       case $ord_var_c == 0x0A:
01179                           $ascii .= '\n';
01180                           break;
01181                       case $ord_var_c == 0x0C:
01182                           $ascii .= '\f';
01183                           break;
01184                       case $ord_var_c == 0x0D:
01185                           $ascii .= '\r';
01186                           break;
01187 
01188                       case $ord_var_c == 0x22:
01189                       case $ord_var_c == 0x2F:
01190                       case $ord_var_c == 0x5C:
01191                           // double quote, slash, slosh
01192                           $ascii .= '\\'.$var{$c};
01193                           break;
01194 
01195                       case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
01196                           // characters U-00000000 - U-0000007F (same as ASCII)
01197                           $ascii .= $var{$c};
01198                           break;
01199 
01200                       case (($ord_var_c & 0xE0) == 0xC0):
01201                           // characters U-00000080 - U-000007FF, mask 110XXXXX
01202                           // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
01203                           $char = pack('C*', $ord_var_c, ord($var{$c + 1}));
01204                           $c += 1;
01205                           $utf16 = $this->json_utf82utf16($char);
01206                           $ascii .= sprintf('\u%04s', bin2hex($utf16));
01207                           break;
01208 
01209                       case (($ord_var_c & 0xF0) == 0xE0):
01210                           // characters U-00000800 - U-0000FFFF, mask 1110XXXX
01211                           // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
01212                           $char = pack('C*', $ord_var_c,
01213                                        ord($var{$c + 1}),
01214                                        ord($var{$c + 2}));
01215                           $c += 2;
01216                           $utf16 = $this->json_utf82utf16($char);
01217                           $ascii .= sprintf('\u%04s', bin2hex($utf16));
01218                           break;
01219 
01220                       case (($ord_var_c & 0xF8) == 0xF0):
01221                           // characters U-00010000 - U-001FFFFF, mask 11110XXX
01222                           // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
01223                           $char = pack('C*', $ord_var_c,
01224                                        ord($var{$c + 1}),
01225                                        ord($var{$c + 2}),
01226                                        ord($var{$c + 3}));
01227                           $c += 3;
01228                           $utf16 = $this->json_utf82utf16($char);
01229                           $ascii .= sprintf('\u%04s', bin2hex($utf16));
01230                           break;
01231 
01232                       case (($ord_var_c & 0xFC) == 0xF8):
01233                           // characters U-00200000 - U-03FFFFFF, mask 111110XX
01234                           // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
01235                           $char = pack('C*', $ord_var_c,
01236                                        ord($var{$c + 1}),
01237                                        ord($var{$c + 2}),
01238                                        ord($var{$c + 3}),
01239                                        ord($var{$c + 4}));
01240                           $c += 4;
01241                           $utf16 = $this->json_utf82utf16($char);
01242                           $ascii .= sprintf('\u%04s', bin2hex($utf16));
01243                           break;
01244 
01245                       case (($ord_var_c & 0xFE) == 0xFC):
01246                           // characters U-04000000 - U-7FFFFFFF, mask 1111110X
01247                           // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
01248                           $char = pack('C*', $ord_var_c,
01249                                        ord($var{$c + 1}),
01250                                        ord($var{$c + 2}),
01251                                        ord($var{$c + 3}),
01252                                        ord($var{$c + 4}),
01253                                        ord($var{$c + 5}));
01254                           $c += 5;
01255                           $utf16 = $this->json_utf82utf16($char);
01256                           $ascii .= sprintf('\u%04s', bin2hex($utf16));
01257                           break;
01258                   }
01259               }
01260 
01261               return '"'.$ascii.'"';
01262 
01263           case 'array':
01264              /*
01265               * As per JSON spec if any array key is not an integer
01266               * we must treat the the whole array as an object. We
01267               * also try to catch a sparsely populated associative
01268               * array with numeric keys here because some JS engines
01269               * will create an array with empty indexes up to
01270               * max_index which can cause memory issues and because
01271               * the keys, which may be relevant, will be remapped
01272               * otherwise.
01273               *
01274               * As per the ECMA and JSON specification an object may
01275               * have any string as a property. Unfortunately due to
01276               * a hole in the ECMA specification if the key is a
01277               * ECMA reserved word or starts with a digit the
01278               * parameter is only accessible using ECMAScript's
01279               * bracket notation.
01280               */
01281 
01282               // treat as a JSON object
01283               if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
01284                   
01285                   $this->json_objectStack[] = $var;
01286 
01287                   $properties = array_map(array($this, 'json_name_value'),
01288                                           array_keys($var),
01289                                           array_values($var));
01290 
01291                   array_pop($this->json_objectStack);
01292 
01293                   foreach($properties as $property) {
01294                       if($property instanceof Exception) {
01295                           return $property;
01296                       }
01297                   }
01298 
01299                   return '{' . join(',', $properties) . '}';
01300               }
01301 
01302               $this->json_objectStack[] = $var;
01303 
01304               // treat it like a regular array
01305               $elements = array_map(array($this, 'json_encode'), $var);
01306 
01307               array_pop($this->json_objectStack);
01308 
01309               foreach($elements as $element) {
01310                   if($element instanceof Exception) {
01311                       return $element;
01312                   }
01313               }
01314 
01315               return '[' . join(',', $elements) . ']';
01316 
01317           case 'object':
01318               $vars = self::encodeObject($var);
01319 
01320               $this->json_objectStack[] = $var;
01321 
01322               $properties = array_map(array($this, 'json_name_value'),
01323                                       array_keys($vars),
01324                                       array_values($vars));
01325 
01326               array_pop($this->json_objectStack);
01327               
01328               foreach($properties as $property) {
01329                   if($property instanceof Exception) {
01330                       return $property;
01331                   }
01332               }
01333                      
01334               return '{' . join(',', $properties) . '}';
01335 
01336           default:
01337               return null;
01338       }
01339   }
01340 
01350   private function json_name_value($name, $value)
01351   {
01352       // Encoding the $GLOBALS PHP array causes an infinite loop
01353       // if the recursion is not reset here as it contains
01354       // a reference to itself. This is the only way I have come up
01355       // with to stop infinite recursion in this case.
01356       if($name=='GLOBALS'
01357          && is_array($value)
01358          && array_key_exists('GLOBALS',$value)) {
01359         $value['GLOBALS'] = '** Recursion **';
01360       }
01361     
01362       $encoded_value = $this->json_encode($value);
01363 
01364       if($encoded_value instanceof Exception) {
01365           return $encoded_value;
01366       }
01367 
01368       return $this->json_encode(strval($name)) . ':' . $encoded_value;
01369   }
01370 }
 All Data Structures Namespaces Files Functions Variables Enumerations