PageRenderTime 10ms CodeModel.GetById 5ms app.highlight 48ms RepoModel.GetById 1ms app.codeStats 1ms

/protected/commands/js/phpToJS.php

http://github.com/phpnode/YiiJS
PHP | 1640 lines | 1291 code | 101 blank | 248 comment | 267 complexity | d534c991c5c989ce5c02ec6302919494 MD5 | raw file
   1<?php
   2/**
   3 * A woefully naive, context unaware PHP to JavaScript token transformer.
   4 * Relies on php.js for a lot of the built in PHP functions.
   5 * HERE BE DRAGONS!
   6 * THIS IS A BUNCH OF STRING AND ARRAY FUNCTIONS THROWN
   7 * TOGETHER WHEN I SHOULD HAVE BEEN WORKING ON SOMETHING MORE 
   8 * IMPORTANT. THE CODE PRODUCED IS OFTEN MANGLED AND AWFUL!
   9 * ... sorry for shouting, but you're going to have to check generated code
  10 * very closely, major problems:
  11 * 					Strings containing $variables will be mangled
  12 * 					Casting doesn't really work very well
  13 * 					Calls to parent classes will be broken
  14 * 					$_GET/$_POST etc dont work, won't work, can't work
  15 * 					Written with Yii in mind, not often suitable for other codebases
  16 * 
  17 * "60% of the time it works every time."
  18 * 
  19 * Usage:
  20 * <pre>
  21 * $converter = new phpToJS;
  22 * $classToConvert = new phpClass("SomeClassName");
  23 * echo $test->convert();
  24 * </pre>
  25 * 
  26 * @author Charles Pick, but I'm ashamed.
  27 * 
  28 */
  29class phpToJS extends CComponent {
  30	/**
  31	 * A list of declared variables
  32	 * @var array
  33	 */
  34	public $declaredVars = array();
  35	/**
  36	 * An array of PHP tokens and their corresponding JS mappings
  37	 * @var array
  38	 */
  39	public static $tokenMap = array(
  40			"." => "+",
  41			"]" => array("convertArrayPush"),
  42			T_NEW => array("convertNew"),
  43			T_STRING_CAST => array("convertCast"),
  44			T_BOOL_CAST => array("convertCast"),
  45			T_ARRAY_CAST => array("convertCast"),
  46			T_DOUBLE_CAST => array("convertCast"),
  47			T_INT_CAST => array("convertCast"),
  48			T_ARRAY => array("convertArray"),
  49			T_FOREACH => array("convertForEach"),
  50			T_IF => array("convertIf","type" => "if"),
  51			T_ELSE => array("convertElse"),
  52			T_ELSEIF => array("convertIf","type" => "elseif"),
  53			T_VARIABLE => array("convertVariable"),
  54			T_STRING => array("convertFunction"),
  55			T_CATCH => array("convertFunction"),
  56			T_ISSET => array("convertFunction"),
  57			T_UNSET => array("convertFunction"),
  58			T_EMPTY => array("convertFunction"),
  59			T_ECHO => array("convertEcho"),
  60			T_LIST => array("convertList"),
  61			T_RETURN => array("convertReturn"),
  62			T_COMMENT => array("convertComment"),
  63			T_OBJECT_OPERATOR => ".",
  64			T_CONCAT_EQUAL => "+=",
  65			T_DOUBLE_ARROW => ":",
  66			T_DOUBLE_COLON => ".",
  67			T_STATIC => "",
  68			T_OPEN_TAG => "",
  69			"@" => "",
  70		);
  71	/**
  72	 * An array of PHP functions and their corresponding JS equivalents
  73	 * @var array
  74	 */
  75	public static $functionMap = array(
  76		"print_r" => "console.log",
  77		"implode" => array(2 => "{1}.join({0})"),
  78		"explode" => array(2 => "{1}.split({0})"),
  79		"preg_match" => array(2 => "{0}.exec({1})"),
  80		"preg_replace" => array(2 => "{2}.replace({0},{1})"),
  81		"preg_replace_callback" => array(2 => "{2}.replace({0},Yii.getFunction({1}))"),
  82		"preg_split" => array(2 => "{1}.split({0})"),
  83		"strtolower" => array(1 => "{0}.toLowerCase()"),
  84		"strtoupper" => array(1 => "{0}.toUpperCase()"),
  85		"is_string" => array(1 => "typeof({0}) === 'string'"),
  86		"!is_string" => array(1 => "typeof({0}) !== 'string'"),
  87		"is_array" => array(1 => "Object.prototype.toString.call({0}) === '[object Array]'"),
  88		"!is_array" => array(1 => "Object.prototype.toString.call({0}) !== '[object Array]'"),
  89		"is_bool" => array(1 => "typeof({0}) === 'boolean'"),
  90		"!is_bool" => array(1 => "typeof({0}) !== 'boolean'"),
  91		"is_int" => array(1 => "(typeof({0}) === 'number' && ({0} % 1 ? false : true))"),
  92		"!is_int" => array(1 => "(typeof({0}) !== 'number' || ({0} % 1 ? true : false))"),
  93		"is_integer" => array(1 => "(typeof({0}) === 'number' && ({0} % 1 ? false : true))"),
  94		"!is_integer" => array(1 => "(typeof({0}) !== 'number' || ({0} % 1 ? true : false))"),
  95		"is_float" => array(1 => "(typeof({0}) === 'number' && ({0} % 1 ? true : false))"),
  96		"!is_float" => array(1 => "(typeof({0}) !== 'number' || ({0} % 1 ? false : true))"),
  97		"is_object" => array(1 => "(!{0} instanceof Array && {0} !== null && typeof({0}) === 'object')"),
  98		"!is_object" => array(1 => "({0} instanceof Array || {0} === null || typeof({0}) !== 'object')"),
  99		
 100		"substr" => array(2 => "{0}.slice({1})", 3 => "{0}.slice({1}, {2})"),
 101		"count" => array(1 => "{0}.length"),
 102		"strlen" => array(1 => "String({0}).length"),
 103		"isset" => array(1 => "{0} !== undefined"),
 104		"!isset" => array(1 => "{0} === undefined"),
 105		"unset" => array(1 => "delete {0}"),
 106		
 107		"method_exists" => array(2 => "{0}[{1}] !== undefined"),
 108		"property_exists" => array(2 => "{0}[{1}] !== undefined"),
 109		'get_class' => array(1 => "{0}.getClassName()"),
 110		"__get" => "get",
 111		"__set" => "set",
 112		"__isset" => "isset",
 113		"__unset" => "unset",
 114		"__call" => "call",
 115		"self" => "this",
 116		"DIRECTORY_SEPARATOR" => "'/'",
 117		"func_num_args" => "arguments.length",
 118		"func_get_args" => "arguments",
 119		
 120		"catch" => array(1 => "catch({0})"),
 121		"import" => "imports",
 122		"mt_rand" => "php.mt_rand",
 123		"rtrim" => "php.rtrim",
 124		"ini_set" => "php.ini_set",
 125		"ini_get" => "php.ini_get",
 126		"ctype_digit" => "php.ctype_digit",
 127		"gmmktime" => "php.gmmktime",
 128		"getdate" => "php.getdate",
 129		"checkdate" => "php.checkdate",
 130		"gmdate" => "php.gmdate",
 131		"strrpos" => "php.strrpos",
 132		'array_chunk' => 'php.array_chunk',
 133		'array_combine' => 'php.array_combine',
 134		'array_diff' => 'php.array_diff',
 135		'array_fill' => 'php.array_fill',
 136		'array_fill_keys' => 'php.array_fill_keys',
 137		'array_filter' => 'php.array_filter',
 138		'array_flip' => 'php.array_flip',
 139		'array_intersect' => 'php.array_intersect',
 140		'array_key_exists' => 'php.array_key_exists',
 141		'array_keys' => 'php.array_keys',
 142		'array_map' => 'php.array_map',
 143		'array_merge' => 'php.array_merge',
 144		'array_merge_recursive' => 'php.array_merge_recursive',
 145		'array_pop' => 'php.array_pop',
 146		'array_push' => 'php.array_push',
 147		'array_reduce' => 'php.array_reduce',
 148		'array_reverse' => 'php.array_reverse',
 149		'array_shift' => 'php.array_shift',
 150		'array_slice' => 'php.array_slice',
 151		'array_splice' => 'php.array_splice',
 152		'array_sum' => 'php.array_sum',
 153		'array_unique' => 'php.array_unique',
 154		'array_unshift' => 'php.array_unshift',
 155		'array_values' => 'php.array_values',
 156		'array_walk' => 'php.array_walk',
 157		'array_walk_recursive' => 'php.array_walk_recursive',
 158		'arsort' => 'php.arsort',
 159		'asort' => 'php.asort',
 160		'compact' => 'php.compact',
 161		'count' => 'php.count',
 162		'end' => 'php.end',
 163		'extract' => 'php.extract',
 164		'in_array' => 'php.in_array',
 165		'krsort' => 'php.krsort',
 166		'ksort' => 'php.ksort',
 167		'natcasesort' => 'php.natcasesort',
 168		'natsort' => 'php.natsort',
 169		'range' => 'php.range',
 170		'reset' => 'php.reset',
 171		'rsort' => 'php.rsort',
 172		'shuffle' => 'php.shuffle',
 173		'sizeof' => 'php.sizeof',
 174		'sort' => 'php.sort',
 175		'uasort' => 'php.uasort',
 176		'uksort' => 'php.uksort',
 177		'usort' => 'php.usort',
 178		'class_exists' => 'php.class_exists',
 179		
 180		'method_exists' => 'php.method_exists',
 181		'property_exists' => 'php.property_exists',
 182		'date' => 'php.date',
 183		'microtime' => 'php.microtime',
 184		'mktime' => 'php.mktime',
 185		'strtotime' => 'php.strtotime',
 186		'time' => 'php.time',
 187		'basename' => 'php.basename',
 188		'dirname' => 'php.dirname',
 189		'call_user_func' => 'php.call_user_func',
 190		'call_user_func_array' => 'php.call_user_func_array',
 191		'abs' => 'php.abs',
 192		'base_convert' => 'php.base_convert',
 193		'ceil' => 'php.ceil',
 194		'floor' => 'php.floor',
 195		'max' => 'php.max',
 196		'min' => 'php.min',
 197		'rand' => 'php.rand',
 198		'round' => 'php.round',
 199		'setcookie' => 'php.setcookie',
 200		'setrawcookie' => 'php.setrawcookie',
 201		'addcslashes' => 'php.addcslashes',
 202		'addslashes' => 'php.addslashes',
 203		'chr' => 'php.chr',
 204		'crc32' => 'php.crc32',
 205		'get_html_translation_table' => 'php.get_html_translation_table',
 206		'html_entity_decode' => 'php.html_entity_decode',
 207		'htmlentities' => 'php.htmlentities',
 208		'htmlspecialchars' => 'php.htmlspecialchars',
 209		'htmlspecialchars_decode' => 'php.htmlspecialchars_decode',
 210		'lcfirst' => 'php.lcfirst',
 211		'ltrim' => 'php.ltrim',
 212		'md5' => 'php.md5',
 213		'nl2br' => 'php.nl2br',
 214		'number_format' => 'php.number_format',
 215		'ord' => 'php.ord',
 216		'parse_str' => 'php.parse_str',
 217		'printf' => 'php.printf',
 218		'quotemeta' => 'php.quotemeta',
 219		'sha1' => 'php.sha1',
 220		'sprintf' => 'php.sprintf',
 221		'str_ireplace' => 'php.str_ireplace',
 222		'str_pad' => 'php.str_pad',
 223		'str_repeat' => 'php.str_repeat',
 224		'str_replace' => 'php.str_replace',
 225		'str_word_count' => 'php.str_word_count',
 226		'strcasecmp' => 'php.strcasecmp',
 227		'strcmp' => 'php.strcmp',
 228		'strcspn' => 'php.strcspn',
 229		'strip_tags' => 'php.strip_tags',
 230		'stripos' => 'php.stripos',
 231		'stripslashes' => 'php.stripslashes',
 232		'stristr' => 'php.stristr',
 233		'strlen' => 'php.strlen',
 234		'strnatcasecmp' => 'php.strnatcasecmp',
 235		'strnatcmp' => 'php.strnatcmp',
 236		'strncasecmp' => 'php.strncasecmp',
 237		'strncmp' => 'php.strncmp',
 238		'strpos' => 'php.strpos',
 239		'strtok' => 'php.strtok',
 240		'strtr' => 'php.strtr',
 241		'substr_compare' => 'php.substr_compare',
 242		'substr_count' => 'php.substr_count',
 243		'substr_replace' => 'php.substr_replace',
 244		'trim' => 'php.trim',
 245		'ucfirst' => 'php.ucfirst',
 246		'ucwords' => 'php.ucwords',
 247		'base64_decode' => 'php.base64_decode',
 248		'base64_encode' => 'php.base64_encode',
 249		'http_build_query' => 'php.http_build_query',
 250		'parse_url' => 'php.parse_url',
 251		'urldecode' => 'php.urldecode',
 252		'urlencode' => 'php.urlencode',
 253		'empty' => 'php.empty',
 254		'gettype' => 'php.gettype',
 255		'intval' => 'php.intval',
 256		'is_callable' => 'php.is_callable',
 257		'is_float' => 'php.is_float',
 258		'is_int' => 'php.is_int',
 259		'utf8_decode' => 'php.utf8_decode',
 260		'utf8_encode' => 'php.utf8_encode'
 261	);	
 262	
 263	/**
 264	 * Converts an array of PHP tokens to JavaScript tokens where possible
 265	 * @param array $tokens the PHP tokens to convert
 266	 * @return array The JavaScript tokens
 267	 */
 268	public function convertTokens($tokens) {
 269		
 270		for($i = array_shift(array_keys($tokens)); $i < count($tokens); $i++ ) {
 271			$token = $tokens[$i];
 272			if (is_array($token) && isset(self::$tokenMap[$token[0]])) { 
 273				$map = self::$tokenMap[$token[0]];
 274				if (is_array($map)) {
 275					$funcName = array_shift($map);
 276					$tokens = $this->{$funcName}($i,$tokens,$map);
 277				}
 278				else {
 279					$tokens[$i][1] = $map;
 280				}
 281			}
 282			elseif (!is_array($token) && isset(self::$tokenMap[$token])) {
 283				$map = self::$tokenMap[$token];
 284				if (is_array($map)) {
 285					$funcName = array_shift($map);
 286					$tokens = $this->{$funcName}($i,$tokens,$map);
 287				}
 288				else {
 289					$tokens[$i] = $map;
 290				}
 291			}
 292		}
 293		return $tokens;
 294	}
 295	
 296	/**
 297	 * Converts a PHP function call to it's JavaScript equivalent if possible
 298	 * @param integer $pos The current position in the list of tokens
 299	 * @param array $tokens The list of tokens
 300	 * @param array $params Extra parameters to pass to this function
 301	 */
 302	protected function convertFunction($pos, $tokens, $params = array()) {
 303		$funcName = strtolower($tokens[$pos][1]);
 304		
 305		// see if this is a not
 306		$not = false;
 307		$last = $pos - 1;
 308		if (isset($tokens[$last]) && $tokens[$last] == "!") {
 309			$not = true;
 310		}
 311		elseif(isset($tokens[$last]) && is_array($tokens[$last]) && $tokens[$last][0] == T_WHITESPACE) {
 312			$last -= 1;
 313			if (isset($tokens[$last]) && $tokens[$last] == "!") {
 314				$not = true;
 315			}
 316		}
 317		if ($not) {
 318			if (isset(self::$functionMap["!".$funcName])) {
 319				$funcName = "!".$funcName;
 320				$tokens[$last] = "";
 321			}
 322		}
 323		if (isset(self::$functionMap[$funcName])) {
 324			
 325			if (is_array(self::$functionMap[$funcName])) {
 326				
 327				$stream = array();
 328				$methodParams = array();
 329				$bracketStack = array();
 330				$stack = array();
 331				for($i = $pos + 1; $i < count($tokens); $i++) {
 332					$stream[] = $i;
 333					$token = $tokens[$i];
 334					switch($token) {
 335						case "(":
 336							$bracketStack[] = $i;
 337							if (count($bracketStack) == 1) {
 338								continue 2;
 339							}
 340							break;
 341						case ")":
 342							$lastBracket = array_pop($bracketStack);
 343							if (count($bracketStack) == 0) {
 344								if (count($stack)) {
 345									$methodParams[] = $stack;
 346								}
 347								break 2;
 348							}
 349							break;
 350						case ",":
 351							if (count($bracketStack) == 1) {
 352								$methodParams[] = $stack;
 353								$stack = array();
 354								continue 2;
 355							}
 356							break;
 357						
 358					}
 359					
 360					$stack[] = $token;
 361				}
 362				$outTokens = array();
 363				$template = array();
 364				foreach($methodParams as $n => $p) {
 365					$template["{".$n."}"] = $this->printTokens($this->convertTokens($p));
 366				}
 367				$which = count($methodParams);
 368				if (!isset(self::$functionMap[$funcName][$which])) {
 369					$which = max(array_keys(self::$functionMap[$funcName]));
 370				}
 371				
 372				if ($funcName == "catch" && count($template) == 1) {
 373					if (strstr($template["{0}"]," ")) {
 374						$template["{0}"] = array_pop(explode(" ",$template["{0}"]));
 375					}
 376				}
 377				$tokens[$pos][1] = trim(strtr(self::$functionMap[$funcName][$which],$template));
 378				
 379				foreach($stream as $k) {
 380					$tokens[$k] = "";
 381				} 
 382			}
 383			else {
 384				$tokens[$pos][1] = self::$functionMap[$funcName];
 385			}
 386		}
 387		else {
 388			
 389			if (substr($tokens[$pos][1],0,1) == "C") {
 390				$tokens[$pos][1] = "Yii.".$tokens[$pos][1];
 391			}
 392			
 393		}
 394		return $tokens;
 395	}
 396	
 397	/**
 398	 * Converts a PHP echo to a JavaScript document.write()
 399	 * @param integer $pos The current position in the list of tokens
 400	 * @param array $tokens The list of tokens
 401	 * @param array $params Extra parameters to pass to this function
 402	 */
 403	protected function convertEcho($pos, $tokens, $params = array()) {
 404		$tokens[$pos] = "document.write(";
 405		$nextIsWhitespace = true;
 406		for($i = $pos + 1; $i < count($tokens); $i++) {
 407			if ($nextIsWhitespace) {
 408				if (is_array($tokens[$i]) && $tokens[$i][0] == T_WHITESPACE) {
 409					$tokens[$i] = "";
 410				}
 411				elseif ($tokens[$i] == " ") {
 412					$tokens[$i] = "";
 413				}
 414				$nextIsWhitespace = false;
 415			}
 416			if (!is_array($tokens[$i]) && substr($tokens[$i],0,1) == ";") {
 417				$tokens[$i] = ")".$tokens[$i];
 418				break;
 419			}
 420			
 421		}
 422		return $tokens;
 423	}
 424	
 425	/**
 426	 * Converts a PHP list() to a series of JavaScript statements
 427	 * @param integer $pos The current position in the list of tokens
 428	 * @param array $tokens The list of tokens
 429	 * @param array $params Extra parameters to pass to this function
 430	 */
 431	protected function convertList($pos, $tokens, $params = array()) {
 432		$stream = array();
 433		$methodParams = array();
 434		$bracketStack = array();
 435		$stack = array();
 436		
 437		for($i = $pos + 1; $i < count($tokens); $i++) {
 438			$stream[] = $i;
 439			$token = $tokens[$i];
 440			switch($token) {
 441				case "(":
 442					$bracketStack[] = $i;
 443					if (count($bracketStack) == 1) {
 444						continue 2;
 445					}
 446					break;
 447				case ")":
 448					$lastBracket = array_pop($bracketStack);
 449					if (count($bracketStack) == 0) {
 450						if (count($stack)) {
 451							$methodParams[] = $stack;
 452						}
 453						break 2;
 454					}
 455					break;
 456				case ",":
 457					if (count($bracketStack) == 1) {
 458						$methodParams[] = $stack;
 459						$stack = array();
 460						continue 2;
 461					}
 462					break;
 463				
 464			}
 465			
 466			$stack[] = $token;
 467		}
 468		$eqSign = null;
 469		$lastStatement = null;
 470		$statement = array();
 471		$isMethod = false;
 472		for($i = $pos + count($stream); $i < count($tokens); $i++) {
 473			$token = $tokens[$i];
 474			switch($token) {
 475				case "=":
 476					if (!$eqSign) {
 477						$eqSign = $i;
 478						$tokens[$i] = "";
 479					}
 480					break;
 481				case ";":
 482					$lastStatement = $i;
 483					$tokens[$i] = "";
 484					break 2;
 485				default:
 486					if ($eqSign) {
 487						if (!is_array($token) && stristr($token,"(")) {
 488							$isMethod = true;
 489						}
 490						$statement[] = $token;
 491						$tokens[$i] = "";
 492					}
 493					break;
 494			}
 495		}
 496		if ($isMethod) {
 497			$varName = "list";
 498			if (!isset($this->declaredVars[$varName])) {
 499				$this->declaredVars[$varName] = $varName;
 500			}
 501		
 502			$tokens[$pos] = $varName." = ";
 503			$tokens[array_shift($stream)] = $this->printTokens($this->convertTokens($statement)).";\n";
 504		}
 505		else {
 506			$tokens[$pos] = "";
 507			$varName = $this->printTokens($this->convertTokens($statement));
 508		}
 509		
 510		foreach($methodParams as $n => $param) {
 511			$v = $this->printTokens($this->convertTokens($param));
 512			$tokens[array_shift($stream)] = "\t\t".$v." = ".$varName."[".$n."];\n";
 513		}
 514		foreach($stream as $k) {
 515			$tokens[$k] = "";
 516		} 
 517		return $tokens;
 518	}
 519	/**
 520	 * Converts a PHP return to a JavaScript return, splits assignments which
 521	 * are not allowed in JavaScript returns
 522	 * @param integer $pos The current position in the list of tokens
 523	 * @param array $tokens The list of tokens
 524	 * @param array $params Extra parameters to pass to this function
 525	 */
 526	protected function convertReturn($pos, $tokens, $params = array()) {
 527		$limit = count($tokens);
 528		$bracketStack = array();
 529		$matchToken = null;
 530		$openBrackets = array();
 531		$closeBrackets = array();
 532		$nextIsVal = null;
 533		$ifLine = null;
 534		$firstBracket = null;
 535		$lastBracket = null;
 536		
 537		$whiteSpace = null;
 538		$bracketToken = null;
 539		$lastStatement = null;
 540		$eqSign = null;
 541		for($i = $pos + 1; $i < $limit; $i++) {
 542			$token = $tokens[$i];
 543			if (is_array($token) && $token[0] != T_WHITESPACE) {
 544				if ($bracketToken === null) {
 545					$bracketToken = false;
 546				}
 547			}
 548			elseif (is_array($token) && $token[0] == T_WHITESPACE && $bracketToken === null) {
 549				$whiteSpace = $i;
 550			}
 551			elseif (!is_array($token)) {
 552				if (strstr($token,"(")) {
 553					if (!count($bracketStack) && $bracketToken === null) {
 554						$bracketToken = $i;
 555						
 556					}
 557					$bracketStack[] = $i;
 558				}
 559				elseif (strstr($token,";")) {
 560					$lastStatement = $i;
 561					break;
 562				}
 563				elseif (strstr($token,")")) { 
 564					array_pop($bracketStack);
 565					if (!count($bracketStack) && $bracketToken) {
 566						break;
 567					}
 568				}
 569				elseif (strstr($token,"=")) {
 570					if (!count($bracketStack)) {
 571						$eqSign = $i;
 572					}
 573				}
 574			}
 575		}
 576		if (!$bracketToken && $eqSign !== null) {
 577			$tokens[$pos][1] .= " (";
 578			$tokens[$lastStatement] = ")".$tokens[$lastStatement];
 579			if ($whiteSpace !== null) {
 580				$tokens[$whiteSpace] = "";
 581			}
 582			
 583			
 584		}
 585		return $tokens;
 586	}
 587
 588	/**
 589	 * Converts a PHP new to a JavaScript new, ensures () appear at the end 
 590	 * to signify constructor
 591	 * @param integer $pos The current position in the list of tokens
 592	 * @param array $tokens The list of tokens
 593	 * @param array $params Extra parameters to pass to this function
 594	 */
 595	protected function convertNew($pos, $tokens, $params = array()) {
 596		$limit = count($tokens);
 597		$bracketStack = array();
 598		$lastStatement = null;
 599		for($i = $pos + 1; $i < $limit; $i++) {
 600			$token = $tokens[$i];
 601			if (!is_array($token)) {
 602				if (strstr($token,"(")) {
 603					$bracketStack[] = $i;
 604					break;
 605				}
 606				elseif (strstr($token,")")) {
 607					array_pop($bracketStack);
 608					break;
 609				}
 610				elseif (strstr($token,";") || strstr($token,":")) {
 611					$lastStatement = $i;
 612					break;
 613				}
 614				
 615			}
 616		}
 617		if (!count($bracketStack) && $lastStatement) {
 618			
 619			$tokens[$lastStatement] = "()".$tokens[$lastStatement];
 620		}
 621		return $tokens;
 622	}
 623	/**
 624	 * Converts PHP's [] = array push notation to a JavaScript array push notation
 625	 * @param integer $pos The current position in the list of tokens
 626	 * @param array $tokens The list of tokens
 627	 * @param array $params Extra parameters to pass to this function
 628	 */
 629	protected function convertArrayPush($pos, $tokens, $params = array()) {
 630		if ($tokens[$pos - 1] == "[" && $tokens[$pos] == "]") {
 631			// make sure the previous token isn't a =
 632			$last = $pos - 2;
 633			if (is_array($tokens[$last]) && ($tokens[$last][0] == T_VARIABLE || $tokens[$last][0] == T_STRING)) {
 634			
 635				$tokens[$pos - 1] = ".push";
 636				$tokens[$pos] = "(";
 637				// now step through the tokens looking for = and ;
 638				$foundEq = false;
 639				for($i = $pos + 1; $i < count($tokens); $i++) {
 640					if ($tokens[$i] == "=" && !$foundEq) {
 641						if (is_array($tokens[$i - 1]) && $tokens[$i - 1][0] == T_WHITESPACE) {
 642							$tokens[$i - 1] = "";
 643						}
 644						if (is_array($tokens[$i + 1]) && $tokens[$i + 1][0] == T_WHITESPACE) {
 645							$tokens[$i + 1] = "";
 646						}
 647						$tokens[$i] = "";
 648						$foundEq = true;
 649					}
 650					elseif (!is_array($tokens[$i]) && (substr(trim($tokens[$i]),0,1) == ";" || substr(trim($tokens[$i]),0,1) == ":")) {
 651						$tokens[$i] = ")".$tokens[$i];
 652						break;
 653					}
 654				}
 655			}
 656			
 657		}
 658		return $tokens;
 659	}
 660	/**
 661	 * Converts a PHP comment to a JavaScript comment, we need
 662	 * this because JavaScript doesn't support # comments
 663	 * @param integer $pos The current position in the list of tokens
 664	 * @param array $tokens The list of tokens
 665	 * @param array $params Extra parameters to pass to this function
 666	 */
 667	protected function convertComment($pos, $tokens, $params = array()) {
 668		if (substr(trim($tokens[$pos][1]),0,1) == "#") {
 669			$tokens[$pos][1] = "/* ".trim(substr($tokens[$pos][1],1)). " */\n";
 670		}
 671		return $tokens;
 672	}
 673	/**
 674	 * Converts a PHP cast to a JavaScript cast if possible
 675	 * @param integer $pos The current position in the list of tokens
 676	 * @param array $tokens The list of tokens
 677	 * @param array $params Extra parameters to pass to this function
 678	 */
 679	protected function convertCast($pos, $tokens, $params = array()) {
 680		$bracketStack = array();
 681		$openBrackets = array();
 682		$closeBrackets = array();
 683		$nextIsWhiteSpace = false;
 684		for ($i = $pos; $i < count($tokens); $i++) {
 685			$token = $tokens[$i];
 686			if (is_array($token)) {
 687				if ($nextIsWhiteSpace) {
 688					$nextIsWhiteSpace = false;
 689					if ($token[0] == T_WHITESPACE) {
 690						$tokens[$i] = "";
 691					}
 692				}
 693				if ($token[0] == T_STRING_CAST) {
 694					$tokens[$i][1] = "String(";
 695					$nextIsWhiteSpace = true;
 696				}
 697				elseif ($token[0] == T_BOOL_CAST) {
 698					$tokens[$i][1] = "Boolean(";
 699					$nextIsWhiteSpace = true;
 700				}
 701				elseif ($token[0] == T_ARRAY_CAST) {
 702					$tokens[$i][1] = "Array(";
 703					$nextIsWhiteSpace = true;
 704				}
 705				elseif ($token[0] == T_DOUBLE_CAST || $token[0] == T_INT_CAST) {
 706					$tokens[$i][1] = "Number(";
 707					$nextIsWhiteSpace = true;
 708				}
 709				
 710			}
 711			else {
 712				switch($token) {
 713					case ")": 
 714						if (!count($bracketStack)) {
 715							// this is the end of cast
 716							$tokens[$i] .= ")";
 717							break 2;
 718						}
 719						array_pop($bracketStack);
 720						break;
 721					case "(": 
 722						$bracketStack[] = $i;
 723						break;
 724					case ";":
 725					case ":":
 726						if (!count($bracketStack)) {
 727							// this is the end of cast
 728							$tokens[$i] = ")".$token;
 729							break 2;
 730						}
 731						break;
 732				}
 733			}
 734		}
 735		return $tokens;
 736	}
 737	/**
 738	 * Converts a PHP array to a JavaScript array
 739	 * @param integer $pos The current position in the list of tokens
 740	 * @param array $tokens The list of tokens
 741	 * @param array $params Extra parameters to pass to this function
 742	 */
 743	protected function convertArray($pos, $tokens, $params = array()) {
 744		$limit = count($tokens);
 745		$bracketStack = array();
 746		$nextIsArray = false;
 747		$nextIsFunc = false;
 748		$openBrackets = array();
 749		$closeBrackets = array();
 750		$isObject = false;
 751		for($i = $pos; $i < $limit; $i++) {
 752			$token = $tokens[$i];
 753			if ($token == "(") {
 754				if ($nextIsArray || !count($bracketStack)) {
 755					
 756					$tokens[$i] = "[";
 757					$openBrackets[] = $i;
 758				}
 759				$bracketStack[] = $i;
 760			}
 761			elseif ($token == ")") {
 762				array_pop($bracketStack);
 763				if ($nextIsArray || !count($bracketStack)) {
 764					$tokens[$i] = "]";
 765					$nextIsArray = false;
 766					$closeBrackets[] = $i;
 767					if (!count($bracketStack)) {
 768						break;
 769					}
 770				}
 771			}
 772			elseif(is_array($token) && $token[0] == T_ARRAY) {
 773				$tokens[$i] = "";
 774				$nextIsArray = true;
 775			}
 776			elseif(is_array($token) && $token[0] == T_DOUBLE_ARROW) {
 777				$tokens[$i] = ":";
 778				$isObject = true;
 779			}
 780			elseif(is_array($token) && $token[0] == T_STRING) {
 781				$nextIsArray = false;
 782				$nextIsFunc = true;
 783			}
 784			else {
 785				
 786			}
 787		}
 788		if ($isObject) {
 789			if (count($openBrackets) != count($closeBrackets)) {
 790				#print_r($tokens);
 791			}
 792			foreach($openBrackets as $n) {
 793				$tokens[$n] = "{";
 794			}
 795			foreach($closeBrackets as $n) {
 796				$tokens[$n] = "}";
 797			}
 798		}
 799		return $tokens;
 800	}
 801
 802	/**
 803	 * Converts a PHP foreach loop to a JavaScript for loop
 804	 * @param integer $pos The current position in the list of tokens
 805	 * @param array $tokens The list of tokens
 806	 * @param array $params Extra parameters to pass to this function
 807	 */
 808	protected function convertForEach($pos, $tokens, $params = array()) {
 809		$limit = count($tokens);
 810		$bracketStack = array();
 811		$matchToken = null;
 812		$openBrackets = array();
 813		$closeBrackets = array();
 814		$nextIsVal = null;
 815		$keyName = null;
 816		$valueName = null;
 817		$feLine = null;
 818		$firstBracket = null;
 819		$lastBracket = null;
 820		for($i = $pos; $i < $limit; $i++) {
 821			$token = $tokens[$i];
 822			if (is_array($token)) {
 823				switch ($token[0]) {
 824					case T_FOREACH:
 825						$matchToken = $i;
 826						$feLine = $token[2];
 827						break;
 828					case T_VARIABLE:
 829						if ($nextIsVal) {
 830							if ($valueName !== null) {
 831								$keyName = $valueName;
 832							}
 833							$valueName = substr($token[1],1);
 834							$nextIsVal = false;
 835							$tokens[$i] = "";
 836						}
 837						break;
 838					case T_DOUBLE_ARROW:
 839						$nextIsVal = true;
 840						$tokens[$i] = "";
 841						break;
 842					case T_AS:
 843						$nextIsVal = true;
 844						$tokens[$i] = "";
 845						break;
 846					case T_WHITESPACE:
 847						$tokens[$i] = "";
 848						break;
 849				}
 850			}
 851			else {
 852				switch ($token) {
 853					case "(":
 854						if ($firstBracket === null) {
 855							$firstBracket = $i;
 856						}
 857						$bracketStack[] = $i;
 858						break;
 859					case ")":
 860						$lastOpen = array_pop($bracketStack);
 861						if (!count($bracketStack)) {
 862							$tokens[$matchToken] = "for ";
 863							if (!$keyName) {
 864								$keyName = $this->getIteratorVariable();
 865							}
 866							$tokens[$lastOpen] .= $keyName." in ";
 867							$lastBracket = $i;
 868							break 2;
 869						}
 870						break;
 871				}
 872			}
 873		}
 874		$bracketToken = null;
 875		$lastStatement = null;
 876		$lastCurlyBracket = null;
 877		$bracketStack = array();
 878		for($i = $lastBracket; $i < $limit; $i++) {
 879			$token = $tokens[$i];
 880			switch ($token) {
 881				case "{":
 882					if ($bracketToken === null) {
 883						$bracketToken = $i;
 884					}
 885					$bracketStack[] = $i;
 886					break;
 887				case "}":
 888					array_pop($bracketStack);
 889					if (!count($bracketStack)) {
 890						$lastCurlyBracket = $i;
 891						break 2;
 892					}
 893					break;
 894				case ";":
 895					if ($bracketToken === null) { 
 896						$lastStatement = $i;
 897						break 2;
 898					}
 899					break;
 900			}
 901		}
 902		if (!$bracketToken) {
 903			if (!$lastStatement) {
 904				return $tokens;
 905			}
 906			$tokens[$lastBracket] .= " {";
 907			$bracketToken = $lastBracket;
 908			$tokens[$lastStatement] .= "\n\t\t}";
 909			
 910		}
 911		$varName = "";
 912		$convertedTokens = array();
 913		$toRemove = array();
 914		for($i = $firstBracket + 1; $i < $lastBracket; $i++) {
 915			$convertedTokens[] = $tokens[$i];
 916			$toRemove[] = $i;
 917		}
 918		$convertedTokens = $this->convertTokens($convertedTokens);
 919		foreach($convertedTokens as $token) {
 920			if (is_array($token)) {
 921				$varName .= $token[1];
 922			}
 923			else {
 924				$varName .= $token;
 925			}
 926		}
 927		if (strstr($varName,"(")) {
 928			// this is a function call so let's cache the result
 929			$iteratorValue = $varName;
 930			if (strlen($keyName) > 1) {
 931				$varName = lcfirst($keyName).ucfirst($valueName);
 932			}
 933			else {
 934				$varName = lcfirst($valueName)."List";
 935			}
 936			if (isset($this->declaredVars[$varName])) {
 937				$varName = lcfirst($keyName).ucFirst($valueName)."List";
 938				if (isset($this->declaredVars[$varName])) {
 939					$varName = lcfirst($keyName).ucFirst($valueName)."Collection";
 940					if (isset($this->declaredVars[$varName])) {
 941						$varName = lcfirst($keyName).ucFirst($valueName)."_forEach";
 942					}
 943					else {
 944						$this->declaredVars[$varName] = $varName;
 945					}
 946				}
 947				else {
 948					$this->declaredVars[$varName] = $varName;
 949				}
 950			}
 951			else {
 952				$this->declaredVars[$varName] = $varName;
 953			}
 954			
 955			$tokens[$pos] = $varName." = ".$iteratorValue.";\n\t\t".$tokens[$pos];
 956			$tokens[array_shift($toRemove)] = $varName;
 957			foreach($toRemove as $i) {
 958				$tokens[$i] = "";
 959			}
 960		}
 961		$tokens[$bracketToken] .= "\n\t\t\tif (".$varName.".hasOwnProperty(".$keyName.")) {\n";
 962		$tokens[$bracketToken] .= "\n\t\t\t\t".$valueName." = ".$varName."[".$keyName."];\n";
 963		if ($lastCurlyBracket !== null) {
 964			$tokens[$lastCurlyBracket] .= "\n\t\t}";
 965		}
 966		else {
 967			$tokens[$lastStatement] .= "\n\t\t}";
 968		}
 969		return $tokens;
 970	}
 971	/**
 972	 * Gets an unused iterator variable
 973	 * @return string the variable name
 974	 */
 975	public function getIteratorVariable() {
 976		$names = array("i","n","j","k","m","p");
 977		foreach($names as $name) {
 978			if (!isset($this->declaredVars[$name])) {
 979				$this->declaredVars[$name] = $name;
 980				return $name;
 981			}
 982		}
 983		
 984	}
 985	/**
 986	 * Converts a PHP if to a JavaScript if, ensures brackets appear
 987	 * @param integer $pos The current position in the list of tokens
 988	 * @param array $tokens The list of tokens
 989	 * @param array $params Extra parameters to pass to this function
 990	 */
 991	protected function convertIf($pos, $tokens, $params = array()) {
 992		$limit = count($tokens);
 993		$bracketStack = array();
 994		$matchToken = null;
 995		$openBrackets = array();
 996		$closeBrackets = array();
 997		$nextIsVal = null;
 998		$ifLine = null;
 999		$firstBracket = null;
1000		$lastBracket = null;
1001		for($i = $pos; $i < $limit; $i++) {
1002			$token = $tokens[$i];
1003			if (is_array($token)) {
1004				switch ($token[0]) {
1005					case T_IF:
1006						if ($params['type'] == "if") {
1007							$matchToken = $i;
1008							$ifLine = $token[2];
1009						}
1010						break;
1011					case T_ELSEIF:
1012						if ($params['type'] == "elseif") {
1013							$matchToken = $i;
1014							$ifLine = $token[2];
1015							$tokens[$i][1] = "else if";
1016						}
1017						break;
1018					
1019				}
1020			}
1021			else {
1022				switch ($token) {
1023					case "(":
1024						if ($firstBracket === null) {
1025							$firstBracket = $i;
1026						}
1027						$bracketStack[] = $i;
1028						break;
1029					case ")":
1030						$lastOpen = array_pop($bracketStack);
1031						if (!count($bracketStack)) {
1032							
1033							$lastBracket = $i;
1034							break 2;
1035						}
1036						break;
1037				}
1038			}
1039		}
1040		$bracketToken = null;
1041		$lastStatement = null;
1042		for($i = $lastBracket; $i < $limit; $i++) {
1043			if (!isset($tokens[$i])) {
1044				break;
1045			}
1046			$token = $tokens[$i];
1047			if (!is_array($token)) {
1048				switch (trim(substr($token,-1,1))) {
1049					case "{":
1050						$bracketToken = $i;
1051						break 2;
1052					case ";": 
1053						$lastStatement = $i;
1054						break 2;
1055					case "}": 
1056						$lastStatement = $i;
1057						break 2;
1058				}
1059			}
1060			else {
1061				
1062			}
1063			
1064		}
1065		if (!$bracketToken && $lastBracket) {
1066			$tokens[$lastBracket] .= " {";
1067			$bracketToken = $lastBracket;
1068			$tokens[$lastStatement] .= "\n\t\t}";
1069			
1070		}
1071		return $tokens;
1072	}
1073	
1074	/**
1075	 * Converts a PHP else to a JavaScript else, ensures brackets appear
1076	 * @param integer $pos The current position in the list of tokens
1077	 * @param array $tokens The list of tokens
1078	 * @param array $params Extra parameters to pass to this function
1079	 */
1080	protected function convertElse($pos, $tokens, $params = array()) {
1081		$limit = count($tokens);
1082		$bracketStack = array();
1083		$matchToken = null;
1084		$openBrackets = array();
1085		$closeBrackets = array();
1086		$nextIsVal = null;
1087		$ifLine = null;
1088		$firstBracket = null;
1089		$lastBracket = null;
1090		
1091		
1092		$bracketToken = null;
1093		$lastStatement = null;
1094		for($i = $pos + 1; $i < $limit; $i++) {
1095			$token = $tokens[$i];
1096			if (is_array($token) && $token[0] == T_IF) {
1097				return $tokens;
1098			}
1099			switch ($token) {
1100				case "{":
1101					$bracketToken = $i;
1102					break 2;
1103				case ";": 
1104					$lastStatement = $i;
1105					break 2;
1106				case "}": 
1107					$lastStatement = $i;
1108					break 2;
1109			}
1110		}
1111		if (!$bracketToken && $lastStatement) {
1112			$tokens[$pos][1] .= " {";
1113			$tokens[$lastStatement] .= "\n\t\t}";
1114			
1115		}
1116		return $tokens;
1117	}
1118	/**
1119	 * Converts a PHP variable name to a JavaScript variable name
1120	 * @param integer $pos The current position in the list of tokens
1121	 * @param array $tokens The list of tokens
1122	 * @param array $params Extra parameters to pass to this function
1123	 */
1124	protected function convertVariable($pos, $tokens, $params = array()) {
1125		if (is_string($tokens[$pos])) {
1126			return $tokens;
1127		}
1128		
1129		
1130		
1131		if (stristr($tokens[$pos][1],".")) {
1132			
1133			$var = substr(array_shift(explode(".",$tokens[$pos][1])),1);
1134			
1135		}
1136		else {
1137			$var = substr($tokens[$pos][1],1);
1138		}
1139		$sanitized = $this->sanitizeVariableName($var);
1140		if ($sanitized != $var) {
1141			$this->declaredVars[$sanitized] = $sanitized;
1142			$tokens[$pos][1] = $sanitized.substr($tokens[$pos][1],strlen($sanitized) + 1);
1143		}
1144		else {
1145			$this->declaredVars[$var] = $var;
1146			$tokens[$pos][1] = substr($tokens[$pos][1],1);
1147		}
1148		return $tokens;
1149	}	
1150
1151	/**
1152	 * Sanitizes a variable name to ensure it doesn't conflict with JavaScript keywords
1153	 * @param string $name the name of the variable
1154	 * @return string the sanitized name
1155	 */
1156	public function sanitizeVariableName($name) {
1157		static $varNames = array(
1158				'break' => 'breakVar',
1159				'case' => 'caseVar',
1160				'comment' => 'commentVar',
1161				'abstract' => 'abstractVar',
1162				'boolean' => 'booleanVar',
1163				'byte' => 'byteVar',
1164				'char' => 'charVar',
1165				'double' => 'doubleVar',
1166				'FALSE' => 'FALSEVar',
1167				'final' => 'finalVar',
1168				'float' => 'floatVar',
1169				'goto' => 'gotoVar',
1170				'catch' => 'catchVar',
1171				'class' => 'classVar',
1172				'const' => 'constVar',
1173				'debugger' => 'debuggerVar',
1174				'continue' => 'continueVar',
1175				'default' => 'defaultVar',
1176				'delete' => 'deleteVar',
1177				'implements' => 'implementsVar',
1178				'instanceOf' => 'instanceOfVar',
1179				'int' => 'intVar',
1180				'interface' => 'interfaceVar',
1181				'long' => 'longVar',
1182				'native' => 'nativeVar',
1183				'null' => 'nullVar',
1184				'package' => 'packageVar',
1185				'private' => 'privateVar',
1186				'enum' => 'enumVar',
1187				'extends' => 'extendsVar',
1188				'finally' => 'finallyVar',
1189				'super' => 'superVar',
1190				'do' => 'doVar',
1191				'else' => 'elseVar',
1192				'export' => 'exportVar',
1193				'protected' => 'protectedVar',
1194				'public' => 'publicVar',
1195				'short' => 'shortVar',
1196				'static' => 'staticVar',
1197				'synchronized' => 'synchronizedVar',
1198				'throws' => 'throwsVar',
1199				'transient' => 'transientVar',
1200				'TRUE' => 'TRUEVar',
1201				'throw' => 'Throw',
1202				'try' => 'haveAGo',
1203				'for' => 'forVar',
1204				'function' => 'func',
1205				'if' => 'ifVar',
1206				'import' => 'imports',
1207				'in' => 'inVar',
1208				'label' => 'labelVar',
1209				'new' => 'newVar',
1210				'return' => 'returnVar',
1211				'switch' => 'switchVar',
1212				'typeof' => 'typeofVar',
1213				'var' => 'varVar',
1214				'void' => 'voidVar',
1215				'while' => 'whileVar',
1216				'with' => 'withVar',
1217		);
1218		if (isset($varNames[$name])) {
1219			return $varNames[$name];
1220		}
1221		return $name;
1222	}
1223	
1224	
1225	/**
1226	 * Assembles a list of tokens into the appropriate code
1227	 * @return string the detokenized code
1228	 */
1229	public function printTokens($tokens) {
1230		$output = "";
1231		foreach($tokens as $token) {
1232			if (is_array($token)) {
1233				$output .= $token[1];
1234			}
1235			else {
1236				$output .= $token;
1237			}
1238		}
1239		return $output;
1240	}
1241}
1242
1243
1244class phpBase extends CComponent {
1245	/**
1246	 * The level of intentation for this item
1247	 * @var integer
1248	 */
1249	public $indent = 0;
1250	/**
1251	 * Holds the reflection for this item
1252	 * @var Reflection
1253	 */
1254	public $reflection;
1255	
1256	/**
1257	 * Magic method to allow easy access to reflection properties
1258	 */
1259	public function __get($name) {
1260		$methodName = "get".$name;
1261		if (is_object($this->reflection) && method_exists($this->reflection, $methodName)) {
1262			return $this->reflection->{$methodName}();
1263		}
1264		return parent::__get($name);
1265	}
1266	
1267	/**
1268	 * Gets the source code for this item
1269	 * @return string the php source code for this item
1270	 */
1271	public function getSourceCode() {
1272		if ($this->reflection instanceof ReflectionClass) {
1273			$filename = $this->reflection->getFileName();
1274		}
1275		else {
1276			$filename = $this->reflection->getDeclaringClass()->getFileName();
1277		}
1278		$lines = file($filename);
1279		return "<?php ".implode("",array_slice($lines,$this->reflection->getStartLine() -1,$this->reflection->getEndLine() - $this->reflection->getStartLine() +1));
1280	}
1281	
1282	/**
1283	 * Gets all the php tokens for this item
1284	 * @return array the php tokens
1285	 */
1286	public function getTokens() {
1287		return token_get_all($this->getSourceCode());
1288	}
1289	
1290	
1291	
1292	/**
1293	 * Gets the sanitized doc comment for this item
1294	 * @return string the sanitized comments
1295	 */
1296	public function getSanitizedComment() {
1297		$comment = $this->reflection->getDocComment();
1298		$comment=strtr(trim(preg_replace('/^\s*\**( |\t)?/m','',trim($comment,'/'))),"\r",'');
1299		
1300		if ($comment == "") {
1301			return "";
1302		}
1303		
1304		$comment = $this->processTags($comment);
1305		if ($comment === "") {
1306			return "";
1307		}
1308		$comment = preg_replace_callback('#\<pre\>(.+?)\<\/pre\>#s',array($this,"translatePreTags"),$comment);
1309		$return = str_repeat("\t",$this->indent)."/**";
1310		$lines =  preg_split("/((\r(?!\n))|((?<!\r)\n)|(\r\n))/","\n".trim($comment));
1311		$return .= str_repeat("\t",$this->indent).implode("\n".str_repeat("\t",$this->indent)." * ",$lines)."\n";
1312		$return .= str_repeat("\t",$this->indent)." */\n";
1313		return $return;
1314	}
1315	
1316	/**
1317	 * Translates examples to JavaScript
1318	 * @param array $matches the matches from preg_replace_callback
1319	 * @return string the translated examples
1320	 */
1321	public function translatePreTags($matches) {
1322		$conv = new phpToJS;
1323		$tokens = token_get_all("<?php ".$matches[1]);
1324		
1325		return "<pre>".$conv->printTokens($conv->convertTokens($tokens))."</pre>";
1326	}
1327	
1328	/**
1329	 * Processes tags for the given doc comment
1330	 * @param string $comment the comment to search for tags
1331	 * @return string The comment with the updated tags
1332	 */
1333	protected function processTags($comment)
1334	{
1335		$tags=preg_split('/^\s*@/m',$comment,-1,PREG_SPLIT_NO_EMPTY);
1336		foreach($tags as $n => $tag)
1337		{
1338			$segs=preg_split('/\s+/',trim($tag),2);
1339			$tagName=$segs[0];
1340			$param=isset($segs[1])?trim($segs[1]):'';
1341			$tagMethod='tag'.ucfirst($tagName);
1342			if(method_exists($this,$tagMethod)) {
1343				$tags[$n] = $this->$tagMethod($param);
1344			}
1345			else {
1346				$tags[$n] = "@".$tag;
1347			}
1348			$comment = str_replace("@$tag",trim($tags[$n])."\n",$comment);
1349		}
1350		
1351		return $comment;
1352	}
1353	/**
1354	 * Converts phpDoc return tag to jsDoc returns tag
1355	 */
1356	protected function tagReturn($param) {
1357		$param = explode(" ",trim($param));
1358		$type = ucfirst(array_shift($param));
1359		if (substr($type,0,1) == "C") {
1360			$type = "Yii.".$type;
1361		}
1362		return "@returns {".$type."} ".implode(" ",$param);
1363	}
1364	
1365	/**
1366	 * Converts phpDoc author tag to jsDoc author tag
1367	 */
1368	protected function tagAuthor($param) {
1369		
1370		return "@originalAuthor ".$param;
1371	}
1372	/**
1373	 * Converts phpDoc throws tag to jsDoc throws tag
1374	 */
1375	protected function tagThrows($param) {
1376		$param = explode(" ",trim($param));
1377		$type = ucfirst(array_shift($param));
1378		if (substr($type,0,1) == "C") {
1379			$type = "Yii.".$type;
1380		}
1381		return "@throws {".$type."} ".implode(" ",$param);
1382	}
1383	/**
1384	 * Converts phpDoc param tag to jsDoc param tag
1385	 */
1386	protected function tagParam($param) {
1387		$param = explode(" ",trim($param));
1388		$type = ucfirst(array_shift($param));
1389		if (substr($type,0,1) == "C") {
1390			$type = "Yii.".$type;
1391		}
1392		$var = array_shift($param);
1393		$var = substr($var,1);
1394		$var = phpToJS::sanitizeVariableName($var);
1395		return "@param {".$type."} ".$var." ".implode(" ",$param);
1396	}
1397	/**
1398	 * Converts phpDoc var tag to jsDoc var tag
1399	 */
1400	protected function tagVar($param) {
1401		$param = explode(" ",trim($param));
1402		$type = ucfirst(array_shift($param));
1403		if (substr($type,0,1) == "C") {
1404			$type = "Yii.".$type;
1405		}
1406		return "@var {".$type."} ".implode(" ",$param);
1407	}
1408	
1409	
1410}
1411
1412/**
1413 * Holds information about a PHP class
1414 */
1415class phpClass extends phpBase {
1416	
1417	/**
1418	 * Constructor
1419	 * @param string $className the name of the class this object refers to
1420	 */
1421	public function __construct($className) {
1422		$this->reflection = new ReflectionClass($className);
1423	}
1424	
1425	
1426	/**
1427	 * Converts the class to a JS object
1428	 * @return string The JavaScript code that represents this object
1429	 */
1430	public function convert() {
1431		$properties = array();
1432		$methods = array();
1433		$constants = array();
1434		$parentClass = $this->parentClass;
1435		$constList = $this->constants;
1436		
1437		if ($parentClass !== false) {
1438			
1439			foreach($parentClass->getConstants() as $const => $value) {
1440				
1441				
1442				if (isset($constList[$const]) && $constList[$const] == $value) {
1443					
1444					unset($constList[$const]);
1445				}
1446			}
1447		}
1448		foreach($constList as $const => $value) {
1449			$item = "/**\n";
1450			$item .= " * @const\n";
1451			$item .= " */\n";
1452			$item.= "Yii.".$this->reflection->name.".".$const." = ".CJavaScript::encode($value);
1453			$constants[] = $item;
1454		}
1455		$hasConstructor = false;
1456		foreach($this->properties as $property) {
1457			if ($property->name == "__construct") {
1458				$hasConstructor = true;
1459			}
1460			$property = new phpProperty($property);
1461			
1462			if ($property->declaringClass->name != $this->reflection->name) {
1463				continue;
1464			}
1465			$properties[] = $property->convert();
1466		}
1467		foreach($this->methods as $method) {
1468			$method = new phpMethod($method);
1469			if ($method->declaringClass->name != $this->reflection->name) {
1470				continue;
1471			}
1472			
1473			$methods[] = $method->convert();
1474		}
1475		$comment = trim(rtrim(trim($this->sanitizedComment),"*/"));
1476		if (strlen($comment) == 0) {
1477			$comment .= "/**\n";
1478		}
1479		$comment .= "\n * @author Charles Pick\n";
1480		$comment .= "\n * @class\n";
1481		if ($this->reflection->getParentClass() !== false) {
1482			$comment .= "\n * @extends Yii.".$this->reflection->getParentClass()->name."\n";
1483		}
1484		$comment .= "\n */\n";
1485		$output = "/*global Yii, php, \$, jQuery, alert, clearInterval, clearTimeout, document, event, frames, history, Image, location, name, navigator, Option, parent, screen, setInterval, setTimeout, window, XMLHttpRequest */\n";
1486		$output .= $comment;
1487		
1488		$output .= "Yii.".$this->reflection->name." = function ".$this->reflection->name." () {\n";
1489		if ($hasConstructor) {
1490			$output .= "\tthis.construct();\n";
1491		}
1492		$output .= "};\n";
1493		if ($this->reflection->getParentClass() !== false) {
1494			$output .= "Yii.".$this->reflection->name. ".prototype = new Yii.".$this->reflection->getParentClass()->name."();\n";
1495			$output .= "Yii.".$this->reflection->name. ".prototype.constructor =  Yii.".$this->reflection->name.";\n";
1496		}
1497		
1498		
1499		#$output .= "Yii.".$this->reflection->name.".prototype = /** @lends Yii.".$this->reflection->name.".prototype */{\n";
1500		$output .= implode(";\n",array_merge($constants, $properties, $methods));
1501		$output .= ";";
1502		#$output .= "\n};\n";
1503		
1504		// clean up empty lines
1505		$output = preg_replace("/(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/", "\n", $output);
1506		return $output;
1507	}
1508}
1509
1510/**
1511 * Holds information about a PHP class property
1512 */
1513class phpProperty extends phpBase {
1514	/**
1515	 * The indentation level for this item
1516	 */
1517	public $indent = 0;
1518	
1519	/**
1520	 * Constructor
1521	 * @param ReflectionProperty $property the property this object refers to
1522	 */
1523	public function __construct(ReflectionProperty $property) {
1524		$this->reflection = $property;
1525	}
1526	
1527	/**
1528	 * Converts the property to a JavaScript property
1529	 * @return string the JavaScript that represents this property
1530	 */
1531	public function convert() {
1532		$comment = $this->sanitizedComment;
1533		$conv = new phpToJS;
1534		$name = $conv->sanitizeVariableName($this->reflection->name);
1535		$defaultProperties = $this->declaringClass->getDefaultProperties();
1536		$value = $defaultProperties[$this->reflection->name];
1537		$value = CJavaScript::encode($value,2);
1538		$return = $comment."Yii.".$this->declaringClass->name.".prototype.".$name." = ".$value;
1539		return $return;
1540	}
1541}
1542
1543
1544
1545/**
1546 * Holds information about a PHP class method
1547 */
1548class phpMethod extends phpBase {
1549	/**
1550	 * The indentation level for this item
1551	 */
1552	public $indent = 0;
1553	/**
1554	 * Constructor
1555	 * @param ReflectionMethod $method the method this object refers to
1556	 */
1557	public function __construct(ReflectionMethod $method) {
1558		$this->reflection = $method;
1559	}
1560	/**
1561	 * Get just the tokens from inside this method (overrides parent implementation)
1562	 * @return array the php tokens that make up this method
1563	 */
1564	public function getTokens() {
1565		$tokens = parent::getTokens();
1566		$return = array();
1567		$firstBracket = null;
1568		$lastBracket = null;
1569		$bracketStack = array();
1570		foreach($tokens as $i => $token) {
1571			if ($token == "{") {
1572				if ($firstBracket === null) {
1573					$firstBracket = $i;
1574				}
1575				$bracketStack[] = $i;
1576			}
1577			elseif ($token == "}") {
1578				array_pop($bracketStack);
1579				if (count($bracketStack) == 0) {
1580					$lastBracket = $i;
1581					break;
1582				}
1583			}
1584		}
1585		for($i = $firstBracket + 1; $i < $lastBracket; $i++) {
1586			$return[] = $tokens[$i];
1587		}
1588		return $return;
1589	}
1590	
1591	/**
1592	 * Converts the method to a JavaScript method
1593	 * @return string the JavaScript that represents this property
1594	 */
1595	public function convert() {
1596		$conv = new phpToJS;
1597		$return = "";
1598		$return .= $this->sanitizedComment;
1599		$signature = array();
1600		$params = array();
1601		$defaultValues = array();
1602		foreach($this->parameters as $param) {
1603			$name = $conv->sanitizeVariableName($param->getName());
1604			$signature[] = $name;
1605			if ($param->isDefaultValueAvailable()) {
1606				$defaultValues[$name] = $param->getDefaultValue();
1607			}
1608			$params[$name] = $name;
1609		}
1610		$signature = implode(", ",$signature);
1611		$methodName = $this->reflection->name;
1612		if (substr($methodName,0,2) == "__") {
1613			$methodName = substr($methodName,2);
1614		}
1615		$methodName = $conv->sanitizeVariableName($methodName);
1616		$return .= "Yii.".$this->declaringClass->name.".prototype.".$methodName. " = function (".$signature.") {\n";
1617		
1618		$code = $conv->printTokens($conv->convertTokens($this->tokens));
1619		foreach($conv->declaredVars as $n => $item) {
1620			if (isset($params[$item]) || $item == "this") {
1621				
1622				unset($conv->declaredVars[$n]);
1623			}
1624		}
1625		if (count($conv->declaredVars)) {
1626			$return .= "\t\tvar ".implode(", ",$conv->declaredVars).";\n";
1627		}
1628		foreach($defaultValues as $name => $value) {
1629			$return .= "\t\tif ($name === undefined) {\n";
1630			$return .= "\t\t\t$name = ".CJavaScript::encode($value).";\n";
1631			$return .= "\t\t}\n";
1632		}
1633		
1634		$return .= $code;
1635		$return .= "\t\t\n";
1636		$return .= "\t}";
1637		return $return;
1638	}
1639	
1640}