[ Index ]

PHP Cross Reference of JPSpan 0.4 (beta)

title

Body

[close]

/JPSpan/ -> Serializer.php (source)

   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  


Generated: Fri Nov 26 11:42:46 2004 Cross-referenced by PHPXref 0.6