| [ Index ] |
PHP Cross Reference of JPSpan 0.4 (beta) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Library for serializing PHP variables into Javascript for use with 4 * Javascript eval() 5 * @package JPSpan 6 * @subpackage Serializer 7 * @version $Id: Serializer.php,v 1.3 2004/11/23 11:53:42 harryf Exp $ 8 */ 9 //----------------------------------------------------------------------------- 10 11 /** 12 * Includes 13 */ 14 require_once JPSPAN . 'CodeWriter.php'; 15 //----------------------------------------------------------------------------- 16 17 /** 18 * Define global for mapping PHP types to element generation 19 * classes 20 * @package JPSpan 21 * @subpackage Serializer 22 */ 23 $GLOBALS['_JPSPAN_SERIALIZER_MAP'] = array( 24 'string'=>array( 25 'class'=>'JPSpan_SerializedString', 26 'file'=>NULL 27 ), 28 'integer'=>array( 29 'class'=>'JPSpan_SerializedInteger', 30 'file'=>NULL 31 ), 32 'boolean'=>array( 33 'class'=>'JPSpan_SerializedBoolean', 34 'file'=>NULL 35 ), 36 'double'=>array( 37 'class'=>'JPSpan_SerializedFloat', 38 'file'=>NULL 39 ), 40 'null'=>array( 41 'class'=>'JPSpan_SerializedNull', 42 'file'=>NULL 43 ), 44 'array'=>array( 45 'class'=>'JPSpan_SerializedArray', 46 'file'=>NULL 47 ), 48 'object'=>array( 49 'class'=>'JPSpan_SerializedObject', 50 'file'=>NULL 51 ), 52 'jpspan_error'=>array( 53 'class'=>'JPSpan_SerializedError', 54 'file'=>NULL 55 ), 56 ); 57 //----------------------------------------------------------------------------- 58 59 /** 60 * Serializes PHP data types into a JavaScript string containing an Function 61 * object for use with eval()<br> 62 * Based on Frederic Saunier's JSserializerCLASS<br/> 63 * Example: 64 * <pre> 65 * $myVar = 'Hello World!'; 66 * echo JPSpan_Serializer::serialize($myVar); 67 * // Displays: new Function("var t1 = \'Hello World!\';return t1;"); 68 * </pre> 69 * Use in Javascript would be; 70 * <pre> 71 * var data_serialized = 'new Function("var t1 = \'Hello World!\';return t1;");'; 72 * var data_func = eval(data_serialized); 73 * var data = data_func(); // data now contains string: Hello World! 74 * </pre> 75 * @see http://www.tekool.net/php/js_serializer/ 76 * @package JPSpan 77 * @subpackage Serializer 78 * @access public 79 */ 80 class JPSpan_Serializer { 81 /** 82 * Serializes a PHP data structure into Javascript 83 * @param mixed PHP data structure 84 * @return string data as Javascript 85 * @access public 86 * @static 87 */ 88 function serialize($data) { 89 JPSpan_getTmpVar(TRUE); 90 $code = & new JPSpan_CodeWriter(); 91 $root = & new JPSpan_RootElement($data); 92 $root->generate($code); 93 return $code->toString(); 94 } 95 /** 96 * Adds an entry to the type map 97 * @param string name of type 98 * @param string name of PHP class to map type to 99 * @param string (optional) filename where class can be found 100 * @return void 101 * @access public 102 * @static 103 */ 104 function addType($type,$class,$file=NULL) { 105 $GLOBALS['_JPSPAN_SERIALIZER_MAP'][strtolower($type)] = 106 array ( 107 'class'=>$class, 108 'file'=>$file, 109 ); 110 } 111 /** 112 * Determine the type of a PHP value, returning an object 113 * used to generated a serialized Javascript representation 114 * @param mixed PHP variable 115 * @return object subclass of JPSpan_SerializedElement 116 * @access protected 117 */ 118 function & reflect($data) { 119 $type = strtolower(gettype($data)); 120 if ( $type == 'object' ) { 121 $objtype = strtolower(get_class($data)); 122 if (array_key_exists($objtype,$GLOBALS['_JPSPAN_SERIALIZER_MAP']) ) { 123 $type = $objtype; 124 } 125 } 126 if ( array_key_exists($type,$GLOBALS['_JPSPAN_SERIALIZER_MAP']) ) { 127 $class = $GLOBALS['_JPSPAN_SERIALIZER_MAP'][$type]['class']; 128 $file = $GLOBALS['_JPSPAN_SERIALIZER_MAP'][$type]['file']; 129 if ( !is_null($file) ) { 130 require_once $file; 131 } 132 $element = & new $class(); 133 } else { 134 $element = & new JPSpan_SerializedNull(); 135 } 136 $element->setTmpVar(); 137 $element->setValue($data); 138 return $element; 139 } 140 } 141 //----------------------------------------------------------------------------- 142 143 /** 144 * Function for generating temporary variable names for use in 145 * serialized Javascript. Uses a static counter to keep names 146 * unique 147 * @return string e.g. t2 148 * @access protected 149 * @package JPSpan 150 * @subpackage Serializer 151 */ 152 function JPSpan_getTmpVar($refresh = FALSE) { 153 static $count = 1; 154 if ( !$refresh ) { 155 $name = 't'.$count; 156 $count++; 157 return $name; 158 } 159 $count = 1; 160 } 161 //----------------------------------------------------------------------------- 162 /** 163 * Wraps the generated JavaScript in an anonymous function 164 * @access protected 165 * @package JPSpan 166 * @subpackage Serializer 167 */ 168 class JPSpan_RootElement { 169 170 /** 171 * Data to be serialized 172 * @var mixed 173 * @access private 174 */ 175 var $data; 176 177 /** 178 * @param mixed data to be serialized 179 * @access protected 180 */ 181 function JPSpan_RootElement($data) { 182 $this->data = $data; 183 } 184 185 /** 186 * Triggers code generation for child data structure then wraps 187 * in anonymous function 188 * @param CodeWriter 189 * @return void 190 * @access protected 191 */ 192 function generate(&$code) { 193 194 $child = & JPSpan_Serializer::reflect($this->data); 195 $child->generate($code); 196 197 $code->write('new Function("'.addSlashes($code->toString()).$child->getReturn().'");'); 198 } 199 } 200 201 /** 202 * Base of class hierarchy for generating Javascript 203 * @access protected 204 * @package JPSpan 205 * @subpackage Serializer 206 * @abstract 207 */ 208 class JPSpan_SerializedElement { 209 /** 210 * Value of the element - used only for scalar types 211 * @var mixed 212 * @access private 213 */ 214 var $value; 215 216 /** 217 * Temporary variable name to use in serialized Javascript 218 * @var string 219 * @access private 220 */ 221 var $tmpName; 222 223 /** 224 * Sets the value of the element 225 * @param mixed 226 * @return void 227 * @access protected 228 */ 229 function setValue($value) { 230 $this->value = $value; 231 } 232 233 /** 234 * Sets the temporary variable name 235 * @return void 236 * @access protected 237 */ 238 function setTmpVar() { 239 $this->tmpName = JPSpan_getTmpVar(); 240 } 241 242 /** 243 * JavaScript string to return if this is the root data element 244 * Called from JPSpan_RootElement::generate 245 * @return string 246 * @access protected 247 */ 248 function getReturn() { 249 return 'return '.$this->tmpName.';'; 250 } 251 252 /** 253 * Template method for generating code 254 * @param JPSpan_CodeWriter 255 * @return void 256 * @access protected 257 */ 258 function generate(&$code) {} 259 260 } 261 //----------------------------------------------------------------------------- 262 263 /** 264 * Generates the representation of a string in Javascript 265 * @package JPSpan 266 * @subpackage Serializer 267 * @access protected 268 */ 269 class JPSpan_SerializedString extends JPSpan_SerializedElement { 270 /** 271 * @param JPSpan_CodeWriter 272 * @return void 273 * @access protected 274 */ 275 function generate(&$code) { 276 $value = addSlashes($this->value); 277 $value = str_replace("\r\n",'\n',$value); 278 $value = str_replace("\n",'\n',$value); 279 $value = str_replace("\t",'\t',$value); 280 $code->append("var {$this->tmpName} = '$value';"); 281 } 282 } 283 //----------------------------------------------------------------------------- 284 285 /** 286 * Generates the representation of a boolean value in Javascript 287 * @package JPSpan 288 * @subpackage Serializer 289 * @access protected 290 */ 291 class JPSpan_SerializedBoolean extends JPSpan_SerializedElement { 292 /** 293 * @param JPSpan_CodeWriter 294 * @return void 295 * @access protected 296 */ 297 function generate(&$code) { 298 if ( $this->value ) { 299 $code->append("var {$this->tmpName} = true;"); 300 } else { 301 $code->append("var {$this->tmpName} = false;"); 302 } 303 } 304 } 305 //----------------------------------------------------------------------------- 306 307 /** 308 * Generates the representation of an integer value in Javascript 309 * @package JPSpan 310 * @subpackage Serializer 311 * @access protected 312 */ 313 class JPSpan_SerializedInteger extends JPSpan_SerializedElement { 314 /** 315 * @param JPSpan_CodeWriter 316 * @return void 317 * @access protected 318 */ 319 function generate(&$code) { 320 $code->append("var {$this->tmpName} = parseInt('{$this->value}');"); 321 } 322 } 323 //----------------------------------------------------------------------------- 324 325 /** 326 * Generates the representation of a float value in Javascript 327 * @package JPSpan 328 * @subpackage Serializer 329 * @access protected 330 */ 331 class JPSpan_SerializedFloat extends JPSpan_SerializedElement { 332 /** 333 * @param JPSpan_CodeWriter 334 * @return void 335 * @access protected 336 */ 337 function generate(&$code) { 338 $code->append("var {$this->tmpName} = parseFloat('{$this->value}');"); 339 } 340 } 341 //----------------------------------------------------------------------------- 342 343 /** 344 * Generates the representation of a null value in Javascript 345 * @package JPSpan 346 * @subpackage Serializer 347 * @access protected 348 */ 349 class JPSpan_SerializedNull extends JPSpan_SerializedElement { 350 /** 351 * @param JPSpan_CodeWriter 352 * @return void 353 * @access protected 354 */ 355 function generate(&$code) { 356 $code->append("var {$this->tmpName} = null;"); 357 } 358 } 359 //----------------------------------------------------------------------------- 360 361 /** 362 * Generates the representation of an array in Javascript 363 * @package JPSpan 364 * @subpackage Serializer 365 * @access protected 366 */ 367 class JPSpan_SerializedArray extends JPSpan_SerializedElement { 368 /** 369 * Representations of the elements of the array 370 * @var array 371 * @access private 372 */ 373 var $children = array(); 374 /** 375 * @param mixed 376 * @return void 377 * @access protected 378 */ 379 function setValue($value) { 380 foreach ( $value as $key => $value ) { 381 $this->children[$key] = & JPSpan_Serializer::reflect($value); 382 } 383 } 384 /** 385 * @param JPSpan_CodeWriter 386 * @return void 387 * @access protected 388 */ 389 function generate(&$code) { 390 $code->append("var {$this->tmpName} = new Array();"); 391 foreach ( array_keys($this->children) as $key ) { 392 $this->children[$key]->generate($code); 393 $tmpName = $this->children[$key]->tmpName; 394 // Spot the difference between index and hash keys.. 395 if ( preg_match('/^[0-9]+$/',$key) ) { 396 $code->append("{$this->tmpName}[$key] = $tmpName;"); 397 } else { 398 $code->append("{$this->tmpName}['$key'] = $tmpName;"); 399 } 400 } 401 402 // Override Javascript toString to display hash values 403 $toString = "function() { "; 404 $toString.= "var str = '[';"; 405 $toString.= "var sep = '';"; 406 $toString.= "for (var prop in this) { "; 407 $toString.= "if (prop == 'toString') { continue; }"; 408 $toString.= "str+=sep+prop+': '+this[prop];"; 409 $toString.= "sep = ', ';"; 410 $toString.= "} return str+']';"; 411 $toString.= "}"; 412 413 $code->append("{$this->tmpName}.toString = $toString;"); 414 } 415 } 416 //----------------------------------------------------------------------------- 417 418 /** 419 * Generates the representation of an object in Javascript 420 * @package JPSpan 421 * @subpackage Serializer 422 * @access protected 423 */ 424 class JPSpan_SerializedObject extends JPSpan_SerializedElement { 425 /** 426 * Name for Javascript object 427 * @var string (= Object) 428 * @access private 429 */ 430 var $classname = 'Object'; 431 /** 432 * Representations of the properties of the object 433 * @var array 434 * @access private 435 */ 436 var $children = array(); 437 /** 438 * @param mixed 439 * @return void 440 * @access protected 441 */ 442 function setValue($value) { 443 $this->setChildValues($value); 444 } 445 /** 446 * Called from setValue. Sets the value of all children of 447 * an object 448 * @param mixed value 449 * @return void 450 * @access protected 451 */ 452 function setChildValues($value) { 453 $properties = get_object_vars($value); 454 foreach ( array_keys($properties) as $property ) { 455 $this->children[$property] = & JPSpan_Serializer::reflect($value->$property); 456 } 457 } 458 /** 459 * @param JPSpan_CodeWriter 460 * @return void 461 * @access protected 462 */ 463 function generate(&$code) { 464 $code->append('var '.$this->tmpName.' = new '.$this->classname.'();'); 465 $this->generateChildren($code); 466 } 467 /** 468 * Called from generate. Invokes generate on each child 469 * of the object 470 * @param JPSpan_CodeWriter 471 * @return void 472 * @access protected 473 */ 474 function generateChildren(&$code) { 475 foreach ( array_keys($this->children) as $key ) { 476 $this->children[$key]->generate($code); 477 $tmpName = $this->children[$key]->tmpName; 478 if ( preg_match('/^[0-9]+$/',$key) ) { 479 $code->append("{$this->tmpName}[$key] = $tmpName;"); 480 } else { 481 $code->append("{$this->tmpName}.$key = $tmpName;"); 482 } 483 } 484 } 485 } 486 //----------------------------------------------------------------------------- 487 488 /** 489 * Generates the representation of a JPSpan_Error object. 490 * Note that you can only generate a single error and that it will 491 * erase all other generated code (the first error in a data structure 492 * will be that which be generated) 493 * @package JPSpan 494 * @subpackage Serializer 495 * @access protected 496 */ 497 class JPSpan_SerializedError { 498 499 /** 500 * Name of Javascript Error class 501 * @var string 502 * @access private 503 */ 504 var $name; 505 506 /** 507 * Error message 508 * @var string 509 * @access private 510 */ 511 var $message; 512 513 /** 514 * Obey interface 515 * @var string 516 * @access private 517 */ 518 var $tmpName = ''; 519 520 /** 521 * @param mixed 522 * @return void 523 * @access protected 524 */ 525 function setValue($error) { 526 $this->code = $error->code; 527 $this->name = $error->name; 528 $this->message = strip_tags($error->message); 529 $this->message = str_replace("'",'',$this->message); 530 $this->message = str_replace('"','',$this->message); 531 } 532 533 /** 534 * Conform to interface 535 * @return void 536 * @access protected 537 */ 538 function setTmpVar() {} 539 540 /** 541 * Errors do no return - exception thrown 542 * @ return string empty 543 * @access protected 544 */ 545 function getReturn() { 546 return ''; 547 } 548 549 /** 550 * @param JPSpan_CodeWriter 551 * @return void 552 * @access protected 553 */ 554 function generate(&$code) { 555 556 $error = "var e = new Error('{$this->message}');"; 557 $error .= "e.name = '{$this->name}';"; 558 $error .= "e.code = '{$this->code}';"; 559 $error .= "throw e;"; 560 // Wrap in anon function - violates RootElement 561 $code->write('new Function("'.addSlashes($error).'");'); 562 563 // Disable further code writing so only single Error returned 564 $code->enabled = FALSE; 565 } 566 } 567 568
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Fri Nov 26 11:42:46 2004 | Cross-referenced by PHPXref 0.6 |