PageRenderTime 213ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

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