MethodTable.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  1. <?php
  2. /**
  3. * Creates the methodTable for a service class.
  4. *
  5. * @usage $this->methodTable = MethodTable::create($this);
  6. * @author Christophe Herreman
  7. * @since 05/01/2005
  8. * @version $id$
  9. *
  10. * Special contributions by Allessandro Crugnola and Ted Milker
  11. */
  12. if (!defined('T_ML_COMMENT')) {
  13. define('T_ML_COMMENT', T_COMMENT);
  14. } else {
  15. define('T_DOC_COMMENT', T_ML_COMMENT);
  16. }
  17. function strrstr($haystack, $needle)
  18. {
  19. return substr($haystack, 0, strpos($haystack.$needle,$needle));
  20. }
  21. function strstrafter($haystack, $needle)
  22. {
  23. return substr(strstr($haystack, $needle), strlen($needle));
  24. }
  25. class MethodTable
  26. {
  27. /**
  28. * Constructor.
  29. *
  30. * Since this class should only be accessed through the static create() method
  31. * this constructor should be made private. Unfortunately, this is not possible
  32. * in PHP4.
  33. *
  34. * @access private
  35. */
  36. function MethodTable(){
  37. }
  38. /**
  39. * Creates the methodTable for a passed class.
  40. *
  41. * @static
  42. * @access public
  43. * @param $className(String) The name of the service class.
  44. * May also simply be __FILE__
  45. * @param $servicePath(String) The location of the classes (optional)
  46. */
  47. function create($className, $servicePath = NULL, &$classComment){
  48. $methodTable = array();
  49. if(file_exists($className))
  50. {
  51. //The new __FILE__ way of doing things was used
  52. $sourcePath = $className;
  53. $className = str_replace("\\", '/', $className);
  54. $className = substr($className, strrpos($className, '/') + 1);
  55. $className = str_replace('.php', '', $className);
  56. }
  57. else
  58. {
  59. $className = str_replace('.php', '', $className);
  60. $fullPath = str_replace('.', '/', $className);
  61. $className = $fullPath;
  62. if(strpos($fullPath, '/') !== FALSE)
  63. {
  64. $className = substr(strrchr($fullPath, '/'), 1);
  65. }
  66. if($servicePath == NULL)
  67. {
  68. if(isset($GLOBALS['amfphp']['classPath']))
  69. {
  70. $servicePath = $GLOBALS['amfphp']['classPath'];
  71. }
  72. else
  73. {
  74. $servicePath = "../services/";
  75. }
  76. }
  77. $sourcePath = $servicePath . $fullPath . ".php";
  78. }
  79. if(!file_exists($sourcePath))
  80. {
  81. trigger_error("The MethodTable class could not find {" .
  82. $sourcePath . "}",
  83. E_USER_ERROR);
  84. }
  85. if(class_exists('ReflectionClass'))
  86. {
  87. //PHP5
  88. $classMethods = MethodTable::getClassMethodsReflection($sourcePath, $className, $classComment);
  89. }
  90. else
  91. {
  92. //PHP4
  93. $classMethods = MethodTable::getClassMethodsTokenizer($sourcePath, $className, $classComment);
  94. }
  95. foreach ($classMethods as $key => $value) {
  96. if($value['name'][0] == '_' || $value['name'] == 'beforeFilter')
  97. {
  98. continue;
  99. }
  100. $methodSignature = $value['args'];
  101. $methodName = $value['name'];
  102. $methodComment = $value['comment'];
  103. $description = MethodTable::getMethodDescription($methodComment) . " " . MethodTable::getMethodCommentAttribute($methodComment, "desc");
  104. $description = trim($description);
  105. $access = MethodTable::getMethodCommentAttributeFirstWord($methodComment, "access");
  106. $roles = MethodTable::getMethodCommentAttributeFirstWord($methodComment, "roles");
  107. $instance = MethodTable::getMethodCommentAttributeFirstWord($methodComment, "instance");
  108. $returns = MethodTable::getMethodCommentAttributeFirstLine($methodComment, "returns");
  109. $pagesize = MethodTable::getMethodCommentAttributeFirstWord($methodComment, "pagesize");
  110. $params = MethodTable::getMethodCommentArguments($methodComment);
  111. //description, arguments, access, [roles, [instance, [returns, [pagesize]]]]
  112. $methodTable[$methodName] = array();
  113. //$methodTable[$methodName]["signature"] = $methodSignature; //debug purposes
  114. $methodTable[$methodName]["description"] = ($description == "") ? "No description given." : $description;
  115. $methodTable[$methodName]["arguments"] = MethodTable::getMethodArguments($methodSignature, $params);
  116. $methodTable[$methodName]["access"] = ($access == "") ? "private" : $access;
  117. if($roles != "") $methodTable[$methodName]["roles"] = $roles;
  118. if($instance != "") $methodTable[$methodName]["instance"] = $instance;
  119. if($returns != "") $methodTable[$methodName]["returns"] = $returns;
  120. if($pagesize != "") $methodTable[$methodName]["pagesize"] = $pagesize;
  121. }
  122. $classComment = trim(str_replace("\r\n", "\n", MethodTable::getMethodDescription($classComment)));
  123. return $methodTable;
  124. }
  125. function getClassMethodsReflection($sourcePath, $className, & $classComment)
  126. {
  127. //Include the class in question
  128. $dir = dirname($sourcePath);
  129. if(!is_dir($dir))
  130. {
  131. return array();
  132. }
  133. chdir($dir);
  134. if(!file_exists($sourcePath))
  135. {
  136. return array();
  137. }
  138. //HACK: eAccelerator
  139. //Check if eAccelator is installed
  140. if( extension_loaded( "eAccelerator" ))
  141. {
  142. //Touch the file so the results of getDocComment will be accurate
  143. touch($sourcePath);
  144. }
  145. $included = include_once($sourcePath);
  146. if($included === FALSE)
  147. {
  148. return array();
  149. }
  150. //Verify that the class exists
  151. if(!class_exists($className))
  152. {
  153. return array();
  154. }
  155. $methodTable = array();
  156. $class = new ReflectionClass($className);
  157. $classComment = $class->getDocComment();
  158. $methods = $class->getMethods();
  159. foreach($methods as $reflectionMethod){
  160. if($reflectionMethod->isPublic() && $method->name[0] != '_' && $method->name != 'beforeFilter')
  161. {
  162. if($reflectionMethod->isConstructor())
  163. {
  164. $classComment .= $reflectionMethod->getDocComment();
  165. }
  166. else
  167. {
  168. $reflectionParameter = $reflectionMethod->getParameters();
  169. $methodTableEntry = array();
  170. $parameters = array();
  171. foreach($reflectionParameter as $parameter){
  172. $parameters[] = $parameter->getName();
  173. }
  174. $methodTableEntry['args'] = '(' . implode(', ', $parameters);
  175. $methodTableEntry['name'] = $reflectionMethod->name;
  176. $methodTableEntry['comment'] = $reflectionMethod->getDocComment();
  177. $methodTable[] = $methodTableEntry;
  178. }
  179. }
  180. }
  181. return $methodTable;
  182. }
  183. function getClassMethodsTokenizer($sourcePath, $className, & $classComment)
  184. {
  185. $source = file_get_contents($sourcePath);
  186. $tokens = token_get_all($source);
  187. $waitingForOpenParenthesis = false;
  188. $waitingForFunction = false;
  189. $waitingForClassName = false;
  190. $bufferingArgs = false;
  191. $argBuffer = "";
  192. $lastFunction = "";
  193. $lastFunctionComment = "";
  194. $lastComment = "";
  195. $classMethods = array();
  196. $realClassName = "";
  197. $openBraces = -10000;
  198. $waitingForEndEncapsedString = false;
  199. foreach($tokens as $token)
  200. {
  201. if (is_string($token)) {
  202. if($token == '{')
  203. {
  204. $openBraces++;
  205. }
  206. if($token == '}')
  207. {
  208. if($waitingForEndEncapsedString)
  209. {
  210. $waitingForEndEncapsedString = false;
  211. }
  212. else
  213. {
  214. $lastComment = '';
  215. $openBraces--;
  216. if($openBraces == 0)
  217. {
  218. //break;
  219. }
  220. }
  221. }
  222. elseif($waitingForOpenParenthesis && $token == '(')
  223. {
  224. $bufferingArgs = true;
  225. $argBuffer = "";
  226. $waitingForOpenParenthesis = false;
  227. }
  228. elseif($bufferingArgs)
  229. {
  230. if($token != ')')
  231. {
  232. $argBuffer .= $token;
  233. }
  234. else
  235. {
  236. if($lastFunction != $realClassName && $lastFunction != "__construct")
  237. {
  238. $classMethods[] = array("name" => $lastFunction,
  239. "comment" => $lastFunctionComment,
  240. "args" => $argBuffer);
  241. }
  242. else
  243. {
  244. $classComment .= "\n\n" . $lastComment;
  245. }
  246. $bufferingArgs = false;
  247. $argBuffer = "";
  248. $lastFunction = "";
  249. $lastFunctionComment = "";
  250. }
  251. }
  252. } else {
  253. // token array
  254. list($id, $text) = $token;
  255. if($bufferingArgs)
  256. {
  257. $argBuffer .= $text;
  258. }
  259. switch ($id)
  260. {
  261. case T_COMMENT:
  262. case T_ML_COMMENT: // we've defined this
  263. case T_DOC_COMMENT: // and this
  264. // no action on comments
  265. $lastComment = $text;
  266. break;
  267. case T_FUNCTION:
  268. if($openBraces >= 1)
  269. {
  270. $waitingForFunction = true;
  271. }
  272. break;
  273. case T_STRING:
  274. if($waitingForFunction)
  275. {
  276. $waitingForFunction = false;
  277. $waitingForOpenParenthesis = true;
  278. $lastFunction = $text;
  279. $lastFunctionComment = $lastComment;
  280. $lastComment = "";
  281. }
  282. if($waitingForClassName)
  283. {
  284. $waitingForClassName = false;
  285. if(strpos(strtolower($className), strtolower($text)) !== FALSE)
  286. {
  287. //Not the class we were looking for
  288. $classComment = $lastComment;
  289. $realClassName = $text;
  290. }
  291. }
  292. break;
  293. case T_CLASS:
  294. $openBraces = 0;
  295. $waitingForClassName = true;
  296. break;
  297. case T_CURLY_OPEN:
  298. case T_DOLLAR_OPEN_CURLY_BRACES:
  299. $waitingForEndEncapsedString = true;
  300. break;
  301. }
  302. }
  303. }
  304. return $classMethods;
  305. }
  306. /**
  307. *
  308. */
  309. function getMethodCommentArguments($comment)
  310. {
  311. $pieces = explode('@param', $comment);
  312. $args = array();
  313. if(is_array($pieces) && count($pieces) > 1)
  314. {
  315. for($i = 0; $i < count($pieces) - 1; $i++)
  316. {
  317. $ps = strrstr($pieces[$i + 1], '@');
  318. $ps = strrstr($ps, '*/');
  319. $args[] = MethodTable::cleanComment($ps);
  320. }
  321. }
  322. return $args;
  323. }
  324. /**
  325. * Returns the description from the comment.
  326. * The description is(are) the first line(s) in the comment.
  327. *
  328. * @static
  329. * @private
  330. * @param $comment(String) The method's comment.
  331. */
  332. function getMethodDescription($comment){
  333. $comment = MethodTable::cleanComment(strrstr($comment, "@"));
  334. return trim($comment);
  335. }
  336. /**
  337. * Returns the value of a comment attribute.
  338. *
  339. * @static
  340. * @private
  341. * @param $comment(String) The method's comment.
  342. * @param $attribute(String) The name of the attribute to get its value from.
  343. */
  344. function getMethodCommentAttribute($comment, $attribute){
  345. $pieces = strstrafter($comment, '@' . $attribute);
  346. if($pieces !== FALSE)
  347. {
  348. $pieces = strrstr($pieces, '@');
  349. $pieces = strrstr($pieces, '*/');
  350. return MethodTable::cleanComment($pieces);
  351. }
  352. return "";
  353. }
  354. /**
  355. * Returns the value of a comment attribute.
  356. *
  357. * @static
  358. * @private
  359. * @param $comment(String) The method's comment.
  360. * @param $attribute(String) The name of the attribute to get its value from.
  361. */
  362. function getMethodCommentAttributeFirstLine($comment, $attribute){
  363. $pieces = strstrafter($comment, '@' . $attribute);
  364. if($pieces !== FALSE)
  365. {
  366. $pieces = strrstr($pieces, '@');
  367. $pieces = strrstr($pieces, "*");
  368. $pieces = strrstr($pieces, "/");
  369. $pieces = strrstr($pieces, "-");
  370. $pieces = strrstr($pieces, "\n");
  371. $pieces = strrstr($pieces, "\r");
  372. $pieces = strrstr($pieces, '*/');
  373. return MethodTable::cleanComment($pieces);
  374. }
  375. return "";
  376. }
  377. function getMethodCommentAttributeFirstWord($comment, $attribute){
  378. $pieces = strstrafter($comment, '@' . $attribute);
  379. if($pieces !== FALSE)
  380. {
  381. $val = MethodTable::cleanComment($pieces);
  382. return trim(strrstr($val, ' '));
  383. }
  384. return "";
  385. }
  386. /**
  387. * Returns an array with the arguments of a method.
  388. *
  389. * @static
  390. * @access private
  391. * @param $methodSignature (String)The method's signatureg;
  392. */
  393. function getMethodArguments($methodSignature, $commentParams){
  394. if(strlen($methodSignature) < 2){
  395. //no arguments, return an empty array
  396. $result = array();
  397. }else{
  398. //clean the arguments before returning them
  399. $result = MethodTable::cleanArguments(explode(",", $methodSignature), $commentParams);
  400. }
  401. return $result;
  402. }
  403. /**
  404. * Cleans the arguments array.
  405. * This method removes all whitespaces and the leading "$" sign from each argument
  406. * in the array.
  407. *
  408. * @static
  409. * @access private
  410. * @param $args(Array) The "dirty" array with arguments.
  411. */
  412. function cleanArguments($args, $commentParams){
  413. $result = array();
  414. foreach($args as $index => $arg){
  415. $arg = strrstr(str_replace('(', '', $arg), '=');
  416. if(!isset($commentParams[$index]))
  417. {
  418. $result[] = trim($arg);
  419. }
  420. else
  421. {
  422. $start = trim($arg);
  423. $end = trim(str_replace('$', '', $commentParams[$index]));
  424. //echo($start);
  425. //echo($end);
  426. if($end != "" && $start != "" && strpos(strtolower($end), strtolower($start)) === 0)
  427. {
  428. $end = substr($end, strlen($start));
  429. }
  430. $result[] = $start . ' - ' . trim($end);
  431. }
  432. }
  433. return $result;
  434. }
  435. /**
  436. * Cleans the comment string by removing all comment start and end characters.
  437. *
  438. * @static
  439. * @private
  440. * @param $comment(String) The method's comment.
  441. */
  442. function cleanComment($comment){
  443. $comment = str_replace("/**", "", $comment);
  444. $comment = str_replace("*/", "", $comment);
  445. $comment = str_replace("*", "", $comment);
  446. $comment = str_replace("\r", "", trim($comment));
  447. $comment = preg_replace("\n[ \t]", "\n", trim($comment));
  448. $comment = str_replace("\n", "\\n", trim($comment));
  449. $comment = preg_replace("[\t ]", " ", trim($comment));
  450. $comment = str_replace("\"", "\\\"", $comment);
  451. return $comment;
  452. }
  453. /**
  454. *
  455. */
  456. function showCode($methodTable){
  457. foreach($methodTable as $methodName=>$methodProps){
  458. $result .= "\n\t\"" . $methodName . "\" => array(";
  459. foreach($methodProps as $key=>$value){
  460. $result .= "\n\t\t\"" . $key . "\" => ";
  461. if($key=="arguments"){
  462. $result .= "array(";
  463. for($i=0; $i<count($value); $i++){
  464. $result .= "\"" . addslashes($value[$i]) . "\"";
  465. if($i<count($value)-1){
  466. $result .= ", ";
  467. }
  468. }
  469. $result .= ")";
  470. }else{
  471. $result .= "\"" . $value . "\"";
  472. }
  473. $result .= ",";
  474. }
  475. $result = substr($result, 0, -1);
  476. $result .= "\n\t),";
  477. }
  478. $result = substr($result, 0, -1);
  479. $result = "\$this->methodTable = array(" . $result;
  480. $result .= "\n);";
  481. return $result;
  482. }
  483. }
  484. ?>