| | 1 | <?php |
|---|
| | 2 | |
|---|
| | 3 | /* ***** BEGIN LICENSE BLOCK ***** |
|---|
| | 4 | * |
|---|
| | 5 | * This file is part of FirePHP (http://www.firephp.org/). |
|---|
| | 6 | * |
|---|
| | 7 | * Software License Agreement (New BSD License) |
|---|
| | 8 | * |
|---|
| | 9 | * Copyright (c) 2006-2008, Christoph Dorn |
|---|
| | 10 | * All rights reserved. |
|---|
| | 11 | * |
|---|
| | 12 | * Redistribution and use in source and binary forms, with or without modification, |
|---|
| | 13 | * are permitted provided that the following conditions are met: |
|---|
| | 14 | * |
|---|
| | 15 | * * Redistributions of source code must retain the above copyright notice, |
|---|
| | 16 | * this list of conditions and the following disclaimer. |
|---|
| | 17 | * |
|---|
| | 18 | * * Redistributions in binary form must reproduce the above copyright notice, |
|---|
| | 19 | * this list of conditions and the following disclaimer in the documentation |
|---|
| | 20 | * and/or other materials provided with the distribution. |
|---|
| | 21 | * |
|---|
| | 22 | * * Neither the name of Christoph Dorn nor the names of its |
|---|
| | 23 | * contributors may be used to endorse or promote products derived from this |
|---|
| | 24 | * software without specific prior written permission. |
|---|
| | 25 | * |
|---|
| | 26 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
|---|
| | 27 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|---|
| | 28 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|---|
| | 29 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR |
|---|
| | 30 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
|---|
| | 31 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|---|
| | 32 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
|---|
| | 33 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|---|
| | 34 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|---|
| | 35 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|---|
| | 36 | * |
|---|
| | 37 | * ***** END LICENSE BLOCK ***** */ |
|---|
| | 38 | |
|---|
| | 39 | |
|---|
| | 40 | |
|---|
| | 41 | /** |
|---|
| | 42 | * Sends the given data to FirePHP Firefox Extension. |
|---|
| | 43 | * The data can be displayed in the Firebug Console or in the |
|---|
| | 44 | * "Server" request tab. |
|---|
| | 45 | * |
|---|
| | 46 | * Usage: |
|---|
| | 47 | * |
|---|
| | 48 | * require('FirePHP.class.php') |
|---|
| | 49 | * |
|---|
| | 50 | * // NOTE: You must have Output Buffering enabled via |
|---|
| | 51 | * // ob_start() or output_buffering ini directive. |
|---|
| | 52 | * |
|---|
| | 53 | * $firephp = FirePHP::getInstance(true); |
|---|
| | 54 | * |
|---|
| | 55 | * $firephp->fb('Hello World'); // Defaults to FirePHP::LOG |
|---|
| | 56 | * |
|---|
| | 57 | * $firephp->fb('Log message' ,FirePHP::LOG); |
|---|
| | 58 | * $firephp->fb('Info message' ,FirePHP::INFO); |
|---|
| | 59 | * $firephp->fb('Warn message' ,FirePHP::WARN); |
|---|
| | 60 | * $firephp->fb('Error message',FirePHP::ERROR); |
|---|
| | 61 | * |
|---|
| | 62 | * $firephp->fb('Message with label','Label',FirePHP::LOG); |
|---|
| | 63 | * |
|---|
| | 64 | * $firephp->fb(array('key1'=>'val1', |
|---|
| | 65 | * 'key2'=>array(array('v1','v2'),'v3')), |
|---|
| | 66 | * 'TestArray',FB_LOG); |
|---|
| | 67 | * |
|---|
| | 68 | * function test($Arg1) { |
|---|
| | 69 | * throw new Exception('Test Exception'); |
|---|
| | 70 | * } |
|---|
| | 71 | * try { |
|---|
| | 72 | * test(array('Hello'=>'World')); |
|---|
| | 73 | * } catch(Exception $e) { |
|---|
| | 74 | * $firephp->fb($e); |
|---|
| | 75 | * } |
|---|
| | 76 | * |
|---|
| | 77 | * $firephp->fb(array('2 SQL queries took 0.06 seconds',array( |
|---|
| | 78 | * array('SQL Statement','Time','Result'), |
|---|
| | 79 | * array('SELECT * FROM Foo','0.02',array('row1','row2')), |
|---|
| | 80 | * array('SELECT * FROM Bar','0.04',array('row1','row2')) |
|---|
| | 81 | * )),FirePHP::TABLE); |
|---|
| | 82 | * |
|---|
| | 83 | * // Will show only in "Server" tab for the request |
|---|
| | 84 | * $firephp->fb(apache_request_headers(),'RequestHeaders',FirePHP::DUMP); |
|---|
| | 85 | * |
|---|
| | 86 | * |
|---|
| | 87 | * @copyright Copyright (C) 2007-2008 Christoph Dorn |
|---|
| | 88 | * @author Christoph Dorn <christoph@christophdorn.com> |
|---|
| | 89 | * @license http://www.opensource.org/licenses/bsd-license.php |
|---|
| | 90 | */ |
|---|
| | 91 | |
|---|
| | 92 | class FirePHP { |
|---|
| | 93 | |
|---|
| | 94 | const LOG = 'LOG'; |
|---|
| | 95 | const INFO = 'INFO'; |
|---|
| | 96 | const WARN = 'WARN'; |
|---|
| | 97 | const ERROR = 'ERROR'; |
|---|
| | 98 | const DUMP = 'DUMP'; |
|---|
| | 99 | const EXCEPTION = 'EXCEPTION'; |
|---|
| | 100 | const TABLE = 'TABLE'; |
|---|
| | 101 | |
|---|
| | 102 | protected static $instance = null; |
|---|
| | 103 | |
|---|
| | 104 | |
|---|
| | 105 | public static function getInstance($AutoCreate=false) { |
|---|
| | 106 | if($AutoCreate===true && !self::$instance) { |
|---|
| | 107 | self::init(); |
|---|
| | 108 | } |
|---|
| | 109 | return self::$instance; |
|---|
| | 110 | } |
|---|
| | 111 | |
|---|
| | 112 | public static function init() { |
|---|
| | 113 | return self::$instance = new self(); |
|---|
| | 114 | } |
|---|
| | 115 | |
|---|
| | 116 | |
|---|
| | 117 | |
|---|
| | 118 | public function setProcessorUrl($URL) |
|---|
| | 119 | { |
|---|
| | 120 | $this->setHeader('X-FirePHP-ProcessorURL', $URL); |
|---|
| | 121 | } |
|---|
| | 122 | |
|---|
| | 123 | public function setRendererUrl($URL) |
|---|
| | 124 | { |
|---|
| | 125 | $this->setHeader('X-FirePHP-RendererURL', $URL); |
|---|
| | 126 | } |
|---|
| | 127 | |
|---|
| | 128 | |
|---|
| | 129 | public function log() { |
|---|
| | 130 | $args = func_get_args(); |
|---|
| | 131 | call_user_func_array(array($this,'fb'),array($args,FirePHP::LOG)); |
|---|
| | 132 | } |
|---|
| | 133 | |
|---|
| | 134 | public function dump($Key, $Variable) { |
|---|
| | 135 | $args = func_get_args(); |
|---|
| | 136 | call_user_func_array(array($this,'fb'),array($Variable,$Key,FirePHP::DUMP)); |
|---|
| | 137 | } |
|---|
| | 138 | |
|---|
| | 139 | public function detectClientExtension() { |
|---|
| | 140 | /* Check if FirePHP is installed on client */ |
|---|
| | 141 | if(!preg_match_all('/\sFirePHP\/([\.|\d]*)\s?/si',$this->getUserAgent(),$m) || |
|---|
| | 142 | !version_compare($m[1][0],'0.0.6','>=')) { |
|---|
| | 143 | return false; |
|---|
| | 144 | } |
|---|
| | 145 | return true; |
|---|
| | 146 | } |
|---|
| | 147 | |
|---|
| | 148 | public function fb($Object) { |
|---|
| | 149 | |
|---|
| | 150 | if (headers_sent($filename, $linenum)) { |
|---|
| | 151 | 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.'); |
|---|
| | 152 | } |
|---|
| | 153 | |
|---|
| | 154 | $Type = null; |
|---|
| | 155 | |
|---|
| | 156 | if(func_num_args()==1) { |
|---|
| | 157 | } else |
|---|
| | 158 | if(func_num_args()==2) { |
|---|
| | 159 | switch(func_get_arg(1)) { |
|---|
| | 160 | case self::LOG: |
|---|
| | 161 | case self::INFO: |
|---|
| | 162 | case self::WARN: |
|---|
| | 163 | case self::ERROR: |
|---|
| | 164 | case self::DUMP: |
|---|
| | 165 | case self::EXCEPTION: |
|---|
| | 166 | case self::TABLE: |
|---|
| | 167 | $Type = func_get_arg(1); |
|---|
| | 168 | break; |
|---|
| | 169 | default: |
|---|
| | 170 | $Object = array(func_get_arg(1),$Object); |
|---|
| | 171 | break; |
|---|
| | 172 | } |
|---|
| | 173 | } else |
|---|
| | 174 | if(func_num_args()==3) { |
|---|
| | 175 | $Type = func_get_arg(2); |
|---|
| | 176 | $Object = array(func_get_arg(1),$Object); |
|---|
| | 177 | } else { |
|---|
| | 178 | throw $this->newException('Wrong number of arguments to fb() function!'); |
|---|
| | 179 | } |
|---|
| | 180 | |
|---|
| | 181 | |
|---|
| | 182 | if(!$this->detectClientExtension()) { |
|---|
| | 183 | return false; |
|---|
| | 184 | } |
|---|
| | 185 | |
|---|
| | 186 | if($Object instanceof Exception) { |
|---|
| | 187 | |
|---|
| | 188 | $Object = array('Class'=>get_class($Object), |
|---|
| | 189 | 'Message'=>$Object->getMessage(), |
|---|
| | 190 | 'File'=>$Object->getFile(), |
|---|
| | 191 | 'Line'=>$Object->getLine(), |
|---|
| | 192 | 'Trace'=>$Object->getTrace()); |
|---|
| | 193 | if($Type===null || $Type===self::EXCEPTION) { |
|---|
| | 194 | $Type = 'TRACE'; |
|---|
| | 195 | } |
|---|
| | 196 | |
|---|
| | 197 | } else { |
|---|
| | 198 | if($Type===null) { |
|---|
| | 199 | $Type = self::LOG; |
|---|
| | 200 | } |
|---|
| | 201 | } |
|---|
| | 202 | |
|---|
| | 203 | $this->setHeader('X-FirePHP-Data-100000000001','{'); |
|---|
| | 204 | if($Type==self::DUMP) { |
|---|
| | 205 | $this->setHeader('X-FirePHP-Data-200000000001','"FirePHP.Dump":{'); |
|---|
| | 206 | $this->setHeader('X-FirePHP-Data-299999999999','"__SKIP__":"__SKIP__"},'); |
|---|
| | 207 | } else { |
|---|
| | 208 | $this->setHeader('X-FirePHP-Data-300000000001','"FirePHP.Firebug.Console":['); |
|---|
| | 209 | $this->setHeader('X-FirePHP-Data-399999999999','["__SKIP__"]],'); |
|---|
| | 210 | } |
|---|
| | 211 | $this->setHeader('X-FirePHP-Data-999999999999','"__SKIP__":"__SKIP__"}'); |
|---|
| | 212 | |
|---|
| | 213 | if($Type==self::DUMP) { |
|---|
| | 214 | $msg = '"'.$Object[0].'":'.$this->json_encode($Object[1]).','; |
|---|
| | 215 | } else { |
|---|
| | 216 | $msg = '["'.$Type.'",'.$this->json_encode($Object).'],'; |
|---|
| | 217 | } |
|---|
| | 218 | |
|---|
| | 219 | foreach( explode("\n",chunk_split($msg, 5000, "\n")) as $part ) { |
|---|
| | 220 | |
|---|
| | 221 | if($part) { |
|---|
| | 222 | |
|---|
| | 223 | usleep(1); /* Ensure microtime() increments with each loop. Not very elegant but it works */ |
|---|
| | 224 | |
|---|
| | 225 | $mt = explode(' ',microtime()); |
|---|
| | 226 | $mt = substr($mt[1],7).substr($mt[0],2); |
|---|
| | 227 | |
|---|
| | 228 | $this->setHeader('X-FirePHP-Data-'.(($Type==self::DUMP)?'2':'3').$mt, $part); |
|---|
| | 229 | } |
|---|
| | 230 | } |
|---|
| | 231 | |
|---|
| | 232 | return true; |
|---|
| | 233 | } |
|---|
| | 234 | |
|---|
| | 235 | protected function setHeader($Name, $Value) { |
|---|
| | 236 | return header($Name.': '.$Value); |
|---|
| | 237 | } |
|---|
| | 238 | |
|---|
| | 239 | protected function getUserAgent() { |
|---|
| | 240 | return $_SERVER['HTTP_USER_AGENT']; |
|---|
| | 241 | } |
|---|
| | 242 | |
|---|
| | 243 | protected function newException($Message) { |
|---|
| | 244 | return new Exception($Message); |
|---|
| | 245 | } |
|---|
| | 246 | |
|---|
| | 247 | |
|---|
| | 248 | /** |
|---|
| | 249 | * Converts to and from JSON format. |
|---|
| | 250 | * |
|---|
| | 251 | * JSON (JavaScript Object Notation) is a lightweight data-interchange |
|---|
| | 252 | * format. It is easy for humans to read and write. It is easy for machines |
|---|
| | 253 | * to parse and generate. It is based on a subset of the JavaScript |
|---|
| | 254 | * Programming Language, Standard ECMA-262 3rd Edition - December 1999. |
|---|
| | 255 | * This feature can also be found in Python. JSON is a text format that is |
|---|
| | 256 | * completely language independent but uses conventions that are familiar |
|---|
| | 257 | * to programmers of the C-family of languages, including C, C++, C#, Java, |
|---|
| | 258 | * JavaScript, Perl, TCL, and many others. These properties make JSON an |
|---|
| | 259 | * ideal data-interchange language. |
|---|
| | 260 | * |
|---|
| | 261 | * This package provides a simple encoder and decoder for JSON notation. It |
|---|
| | 262 | * is intended for use with client-side Javascript applications that make |
|---|
| | 263 | * use of HTTPRequest to perform server communication functions - data can |
|---|
| | 264 | * be encoded into JSON notation for use in a client-side javascript, or |
|---|
| | 265 | * decoded from incoming Javascript requests. JSON format is native to |
|---|
| | 266 | * Javascript, and can be directly eval()'ed with no further parsing |
|---|
| | 267 | * overhead |
|---|
| | 268 | * |
|---|
| | 269 | * All strings should be in ASCII or UTF-8 format! |
|---|
| | 270 | * |
|---|
| | 271 | * LICENSE: Redistribution and use in source and binary forms, with or |
|---|
| | 272 | * without modification, are permitted provided that the following |
|---|
| | 273 | * conditions are met: Redistributions of source code must retain the |
|---|
| | 274 | * above copyright notice, this list of conditions and the following |
|---|
| | 275 | * disclaimer. Redistributions in binary form must reproduce the above |
|---|
| | 276 | * copyright notice, this list of conditions and the following disclaimer |
|---|
| | 277 | * in the documentation and/or other materials provided with the |
|---|
| | 278 | * distribution. |
|---|
| | 279 | * |
|---|
| | 280 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED |
|---|
| | 281 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
|---|
| | 282 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN |
|---|
| | 283 | * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
|---|
| | 284 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
|---|
| | 285 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS |
|---|
| | 286 | * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
|---|
| | 287 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR |
|---|
| | 288 | * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE |
|---|
| | 289 | * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
|---|
| | 290 | * DAMAGE. |
|---|
| | 291 | * |
|---|
| | 292 | * @category |
|---|
| | 293 | * @package Services_JSON |
|---|
| | 294 | * @author Michal Migurski <mike-json@teczno.com> |
|---|
| | 295 | * @author Matt Knapp <mdknapp[at]gmail[dot]com> |
|---|
| | 296 | * @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com> |
|---|
| | 297 | * @author Christoph Dorn <christoph@christophdorn.com> |
|---|
| | 298 | * @copyright 2005 Michal Migurski |
|---|
| | 299 | * @version CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $ |
|---|
| | 300 | * @license http://www.opensource.org/licenses/bsd-license.php |
|---|
| | 301 | * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198 |
|---|
| | 302 | */ |
|---|
| | 303 | |
|---|
| | 304 | |
|---|
| | 305 | /** |
|---|
| | 306 | * Keep a list of objects as we descend into the array so we can detect recursion. |
|---|
| | 307 | */ |
|---|
| | 308 | private $json_objectStack = array(); |
|---|
| | 309 | |
|---|
| | 310 | |
|---|
| | 311 | /** |
|---|
| | 312 | * convert a string from one UTF-8 char to one UTF-16 char |
|---|
| | 313 | * |
|---|
| | 314 | * Normally should be handled by mb_convert_encoding, but |
|---|
| | 315 | * provides a slower PHP-only method for installations |
|---|
| | 316 | * that lack the multibye string extension. |
|---|
| | 317 | * |
|---|
| | 318 | * @param string $utf8 UTF-8 character |
|---|
| | 319 | * @return string UTF-16 character |
|---|
| | 320 | * @access private |
|---|
| | 321 | */ |
|---|
| | 322 | private function json_utf82utf16($utf8) |
|---|
| | 323 | { |
|---|
| | 324 | // oh please oh please oh please oh please oh please |
|---|
| | 325 | if(function_exists('mb_convert_encoding')) { |
|---|
| | 326 | return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); |
|---|
| | 327 | } |
|---|
| | 328 | |
|---|
| | 329 | switch(strlen($utf8)) { |
|---|
| | 330 | case 1: |
|---|
| | 331 | // this case should never be reached, because we are in ASCII range |
|---|
| | 332 | // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
|---|
| | 333 | return $utf8; |
|---|
| | 334 | |
|---|
| | 335 | case 2: |
|---|
| | 336 | // return a UTF-16 character from a 2-byte UTF-8 char |
|---|
| | 337 | // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
|---|
| | 338 | return chr(0x07 & (ord($utf8{0}) >> 2)) |
|---|
| | 339 | . chr((0xC0 & (ord($utf8{0}) << 6)) |
|---|
| | 340 | | (0x3F & ord($utf8{1}))); |
|---|
| | 341 | |
|---|
| | 342 | case 3: |
|---|
| | 343 | // return a UTF-16 character from a 3-byte UTF-8 char |
|---|
| | 344 | // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
|---|
| | 345 | return chr((0xF0 & (ord($utf8{0}) << 4)) |
|---|
| | 346 | | (0x0F & (ord($utf8{1}) >> 2))) |
|---|
| | 347 | . chr((0xC0 & (ord($utf8{1}) << 6)) |
|---|
| | 348 | | (0x7F & ord($utf8{2}))); |
|---|
| | 349 | } |
|---|
| | 350 | |
|---|
| | 351 | // ignoring UTF-32 for now, sorry |
|---|
| | 352 | return ''; |
|---|
| | 353 | } |
|---|
| | 354 | |
|---|
| | 355 | /** |
|---|
| | 356 | * encodes an arbitrary variable into JSON format |
|---|
| | 357 | * |
|---|
| | 358 | * @param mixed $var any number, boolean, string, array, or object to be encoded. |
|---|
| | 359 | * see argument 1 to Services_JSON() above for array-parsing behavior. |
|---|
| | 360 | * if var is a strng, note that encode() always expects it |
|---|
| | 361 | * to be in ASCII or UTF-8 format! |
|---|
| | 362 | * |
|---|
| | 363 | * @return mixed JSON string representation of input var or an error if a problem occurs |
|---|
| | 364 | * @access public |
|---|
| | 365 | */ |
|---|
| | 366 | private function json_encode($var) |
|---|
| | 367 | { |
|---|
| | 368 | |
|---|
| | 369 | if(is_object($var)) { |
|---|
| | 370 | if(in_array($var,$this->json_objectStack)) { |
|---|
| | 371 | return '"** Recursion **"'; |
|---|
| | 372 | } |
|---|
| | 373 | } |
|---|
| | 374 | |
|---|
| | 375 | switch (gettype($var)) { |
|---|
| | 376 | case 'boolean': |
|---|
| | 377 | return $var ? 'true' : 'false'; |
|---|
| | 378 | |
|---|
| | 379 | case 'NULL': |
|---|
| | 380 | return 'null'; |
|---|
| | 381 | |
|---|
| | 382 | case 'integer': |
|---|
| | 383 | return (int) $var; |
|---|
| | 384 | |
|---|
| | 385 | case 'double': |
|---|
| | 386 | case 'float': |
|---|
| | 387 | return (float) $var; |
|---|
| | 388 | |
|---|
| | 389 | case 'string': |
|---|
| | 390 | // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT |
|---|
| | 391 | $ascii = ''; |
|---|
| | 392 | $strlen_var = strlen($var); |
|---|
| | 393 | |
|---|
| | 394 | /* |
|---|
| | 395 | * Iterate over every character in the string, |
|---|
| | 396 | * escaping with a slash or encoding to UTF-8 where necessary |
|---|
| | 397 | */ |
|---|
| | 398 | for ($c = 0; $c < $strlen_var; ++$c) { |
|---|
| | 399 | |
|---|
| | 400 | $ord_var_c = ord($var{$c}); |
|---|
| | 401 | |
|---|
| | 402 | switch (true) { |
|---|
| | 403 | case $ord_var_c == 0x08: |
|---|
| | 404 | $ascii .= '\b'; |
|---|
| | 405 | break; |
|---|
| | 406 | case $ord_var_c == 0x09: |
|---|
| | 407 | $ascii .= '\t'; |
|---|
| | 408 | break; |
|---|
| | 409 | case $ord_var_c == 0x0A: |
|---|
| | 410 | $ascii .= '\n'; |
|---|
| | 411 | break; |
|---|
| | 412 | case $ord_var_c == 0x0C: |
|---|
| | 413 | $ascii .= '\f'; |
|---|
| | 414 | break; |
|---|
| | 415 | case $ord_var_c == 0x0D: |
|---|
| | 416 | $ascii .= '\r'; |
|---|
| | 417 | break; |
|---|
| | 418 | |
|---|
| | 419 | case $ord_var_c == 0x22: |
|---|
| | 420 | case $ord_var_c == 0x2F: |
|---|
| | 421 | case $ord_var_c == 0x5C: |
|---|
| | 422 | // double quote, slash, slosh |
|---|
| | 423 | $ascii .= '\\'.$var{$c}; |
|---|
| | 424 | break; |
|---|
| | 425 | |
|---|
| | 426 | case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)): |
|---|
| | 427 | // characters U-00000000 - U-0000007F (same as ASCII) |
|---|
| | 428 | $ascii .= $var{$c}; |
|---|
| | 429 | break; |
|---|
| | 430 | |
|---|
| | 431 | case (($ord_var_c & 0xE0) == 0xC0): |
|---|
| | 432 | // characters U-00000080 - U-000007FF, mask 110XXXXX |
|---|
| | 433 | // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
|---|
| | 434 | $char = pack('C*', $ord_var_c, ord($var{$c + 1})); |
|---|
| | 435 | $c += 1; |
|---|
| | 436 | $utf16 = $this->json_utf82utf16($char); |
|---|
| | 437 | $ascii .= sprintf('\u%04s', bin2hex($utf16)); |
|---|
| | 438 | break; |
|---|
| | 439 | |
|---|
| | 440 | case (($ord_var_c & 0xF0) == 0xE0): |
|---|
| | 441 | // characters U-00000800 - U-0000FFFF, mask 1110XXXX |
|---|
| | 442 | // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
|---|
| | 443 | $char = pack('C*', $ord_var_c, |
|---|
| | 444 | ord($var{$c + 1}), |
|---|
| | 445 | ord($var{$c + 2})); |
|---|
| | 446 | $c += 2; |
|---|
| | 447 | $utf16 = $this->json_utf82utf16($char); |
|---|
| | 448 | $ascii .= sprintf('\u%04s', bin2hex($utf16)); |
|---|
| | 449 | break; |
|---|
| | 450 | |
|---|
| | 451 | case (($ord_var_c & 0xF8) == 0xF0): |
|---|
| | 452 | // characters U-00010000 - U-001FFFFF, mask 11110XXX |
|---|
| | 453 | // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
|---|
| | 454 | $char = pack('C*', $ord_var_c, |
|---|
| | 455 | ord($var{$c + 1}), |
|---|
| | 456 | ord($var{$c + 2}), |
|---|
| | 457 | ord($var{$c + 3})); |
|---|
| | 458 | $c += 3; |
|---|
| | 459 | $utf16 = $this->json_utf82utf16($char); |
|---|
| | 460 | $ascii .= sprintf('\u%04s', bin2hex($utf16)); |
|---|
| | 461 | break; |
|---|
| | 462 | |
|---|
| | 463 | case (($ord_var_c & 0xFC) == 0xF8): |
|---|
| | 464 | // characters U-00200000 - U-03FFFFFF, mask 111110XX |
|---|
| | 465 | // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
|---|
| | 466 | $char = pack('C*', $ord_var_c, |
|---|
| | 467 | ord($var{$c + 1}), |
|---|
| | 468 | ord($var{$c + 2}), |
|---|
| | 469 | ord($var{$c + 3}), |
|---|
| | 470 | ord($var{$c + 4})); |
|---|
| | 471 | $c += 4; |
|---|
| | 472 | $utf16 = $this->json_utf82utf16($char); |
|---|
| | 473 | $ascii .= sprintf('\u%04s', bin2hex($utf16)); |
|---|
| | 474 | break; |
|---|
| | 475 | |
|---|
| | 476 | case (($ord_var_c & 0xFE) == 0xFC): |
|---|
| | 477 | // characters U-04000000 - U-7FFFFFFF, mask 1111110X |
|---|
| | 478 | // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 |
|---|
| | 479 | $char = pack('C*', $ord_var_c, |
|---|
| | 480 | ord($var{$c + 1}), |
|---|
| | 481 | ord($var{$c + 2}), |
|---|
| | 482 | ord($var{$c + 3}), |
|---|
| | 483 | ord($var{$c + 4}), |
|---|
| | 484 | ord($var{$c + 5})); |
|---|
| | 485 | $c += 5; |
|---|
| | 486 | $utf16 = $this->json_utf82utf16($char); |
|---|
| | 487 | $ascii .= sprintf('\u%04s', bin2hex($utf16)); |
|---|
| | 488 | break; |
|---|
| | 489 | } |
|---|
| | 490 | } |
|---|
| | 491 | |
|---|
| | 492 | return '"'.$ascii.'"'; |
|---|
| | 493 | |
|---|
| | 494 | case 'array': |
|---|
| | 495 | /* |
|---|
| | 496 | * As per JSON spec if any array key is not an integer |
|---|
| | 497 | * we must treat the the whole array as an object. We |
|---|
| | 498 | * also try to catch a sparsely populated associative |
|---|
| | 499 | * array with numeric keys here because some JS engines |
|---|
| | 500 | * will create an array with empty indexes up to |
|---|
| | 501 | * max_index which can cause memory issues and because |
|---|
| | 502 | * the keys, which may be relevant, will be remapped |
|---|
| | 503 | * otherwise. |
|---|
| | 504 | * |
|---|
| | 505 | * As per the ECMA and JSON specification an object may |
|---|
| | 506 | * have any string as a property. Unfortunately due to |
|---|
| | 507 | * a hole in the ECMA specification if the key is a |
|---|
| | 508 | * ECMA reserved word or starts with a digit the |
|---|
| | 509 | * parameter is only accessible using ECMAScript's |
|---|
| | 510 | * bracket notation. |
|---|
| | 511 | */ |
|---|
| | 512 | |
|---|
| | 513 | // treat as a JSON object |
|---|
| | 514 | if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) { |
|---|
| | 515 | |
|---|
| | 516 | $this->json_objectStack[] = $var; |
|---|
| | 517 | |
|---|
| | 518 | $properties = array_map(array($this, 'json_name_value'), |
|---|
| | 519 | array_keys($var), |
|---|
| | 520 | array_values($var)); |
|---|
| | 521 | |
|---|
| | 522 | array_pop($this->json_objectStack); |
|---|
| | 523 | |
|---|
| | 524 | foreach($properties as $property) { |
|---|
| | 525 | if($property instanceof Exeption) { |
|---|
| | 526 | return $property; |
|---|
| | 527 | } |
|---|
| | 528 | } |
|---|
| | 529 | |
|---|
| | 530 | return '{' . join(',', $properties) . '}'; |
|---|
| | 531 | } |
|---|
| | 532 | |
|---|
| | 533 | $this->json_objectStack[] = $var; |
|---|
| | 534 | |
|---|
| | 535 | // treat it like a regular array |
|---|
| | 536 | $elements = array_map(array($this, 'json_encode'), $var); |
|---|
| | 537 | |
|---|
| | 538 | array_pop($this->json_objectStack); |
|---|
| | 539 | |
|---|
| | 540 | foreach($elements as $element) { |
|---|
| | 541 | if($element instanceof Exception) { |
|---|
| | 542 | return $element; |
|---|
| | 543 | } |
|---|
| | 544 | } |
|---|
| | 545 | |
|---|
| | 546 | return '[' . join(',', $elements) . ']'; |
|---|
| | 547 | |
|---|
| | 548 | case 'object': |
|---|
| | 549 | $vars = get_object_vars($var); |
|---|
| | 550 | |
|---|
| | 551 | $this->json_objectStack[] = $var; |
|---|
| | 552 | |
|---|
| | 553 | $properties = array_map(array($this, 'json_name_value'), |
|---|
| | 554 | array_keys($vars), |
|---|
| | 555 | array_values($vars)); |
|---|
| | 556 | |
|---|
| | 557 | array_pop($this->json_objectStack); |
|---|
| | 558 | |
|---|
| | 559 | foreach($properties as $property) { |
|---|
| | 560 | if($property instanceof Exception) { |
|---|
| | 561 | return $property; |
|---|
| | 562 | } |
|---|
| | 563 | } |
|---|
| | 564 | |
|---|
| | 565 | return '{' . join(',', $properties) . '}'; |
|---|
| | 566 | |
|---|
| | 567 | default: |
|---|
| | 568 | return null; |
|---|
| | 569 | } |
|---|
| | 570 | } |
|---|
| | 571 | |
|---|
| | 572 | /** |
|---|
| | 573 | * array-walking function for use in generating JSON-formatted name-value pairs |
|---|
| | 574 | * |
|---|
| | 575 | * @param string $name name of key to use |
|---|
| | 576 | * @param mixed $value reference to an array element to be encoded |
|---|
| | 577 | * |
|---|
| | 578 | * @return string JSON-formatted name-value pair, like '"name":value' |
|---|
| | 579 | * @access private |
|---|
| | 580 | */ |
|---|
| | 581 | private function json_name_value($name, $value) |
|---|
| | 582 | { |
|---|
| | 583 | $encoded_value = $this->json_encode($value); |
|---|
| | 584 | |
|---|
| | 585 | if($encoded_value instanceof Exception) { |
|---|
| | 586 | return $encoded_value; |
|---|
| | 587 | } |
|---|
| | 588 | |
|---|
| | 589 | return $this->json_encode(strval($name)) . ':' . $encoded_value; |
|---|
| | 590 | } |
|---|
| | 591 | |
|---|
| | 592 | } |
|---|
| | 593 | |
|---|
| | 594 | ?> |