1、基于反射机制:实现注释生成文档

实现注释生成文档,主要是依赖反射机制获得成员函数的注释,然后基于注释的规范风格,进行参数切割(不建议使用正则表达式,因为效率太低),然后生成对应的html结构。
生成文档需要对注释风格编写是统一,并且规范才有可能实现,一旦不规范注释,就有可能生成错误。
具体的案例代码如下:

  1. <?php
  2. # 测试用的A类
  3. class TestA {
  4. /**
  5. * 尼妹的
  6. * @author 小黄牛
  7. * @version v1.0.0.1
  8. * @deprecated v1.0.9.* 版本后弃用
  9. * @global 无
  10. * @todo 后期这个函数需要优化到神一样的执行速度
  11. * @param string $name 打死你
  12. * @param string $name 打死你
  13. * @param string $name 打死你
  14. * @return void
  15. */
  16. public function orderBackA() {}
  17. /**
  18. * 你哥的
  19. * 这是一个给小黄牛用的函数,哈哈,不服你来打我啊
  20. *
  21. * @author 小黄牛
  22. * @version v1.0.0.1
  23. * @deprecated v1.0.9.* 版本后弃用
  24. * @global 无
  25. * @todo 后期这个函数需要优化到神一样的执行速度
  26. * @param string $name 打死你
  27. * @param string $name 打死你
  28. * @param string $name 打死你
  29. * @return void
  30. */
  31. private function orderBackB() {}
  32. }
  33. # 文档生成类
  34. class WordApi{
  35. private $_obj_Public;
  36. private $_obj_Private;
  37. private $_obj_Protected;
  38. private $_wordData;
  39. private $_length = 0;
  40. private $_wordGroup = [
  41. '@author' => '作者',
  42. '@version' => '版本',
  43. '@deprecated' => '废弃说明',
  44. '@global' => '全局变量',
  45. '@todo' => '优化建议',
  46. '@param' => '参数',
  47. '@return' => '返回值',
  48. ];
  49. /**
  50. * 自动反射类
  51. *
  52. * @param string $className 类名
  53. */
  54. public function __construct($className) {
  55. # 使用ReflectionClass类
  56. $class = new ReflectionClass($className);
  57. # 获得成员函数
  58. $this->_obj_Public = $class->getMethods(ReflectionMethod::IS_PUBLIC);
  59. $this->_obj_Private = $class->getMethods(ReflectionMethod::IS_PRIVATE);
  60. $this->_obj_Protected = $class->getMethods(ReflectionMethod::IS_PROTECTED);
  61. # 自动触发解析
  62. $this->getFunction();
  63. $this->getNotes();
  64. }
  65. /**
  66. * 反射类下面的所有成员函数名称
  67. */
  68. private function getFunction() {
  69. # 读取不同权限下的方法名称
  70. foreach ($this->_obj_Public as $method) {
  71. $this->_wordData[$this->_length]['obj'] = $method;
  72. $this->_wordData[$this->_length]['class'] = $method->class;
  73. $this->_wordData[$this->_length]['name'] = $method->name;
  74. $this->_wordData[$this->_length]['auto'] = ['控制权限', 'public'];
  75. $this->_length++;
  76. }
  77. foreach ($this->_obj_Private as $method) {
  78. $this->_wordData[$this->_length]['obj'] = $method;
  79. $this->_wordData[$this->_length]['class'] = $method->class;
  80. $this->_wordData[$this->_length]['name'] = $method->name;
  81. $this->_wordData[$this->_length]['auto'] = ['控制权限', 'private'];
  82. $this->_length++;
  83. }
  84. foreach ($this->_obj_Protected as $method) {
  85. $this->_wordData[$this->_length]['obj'] = $method;
  86. $this->_wordData[$this->_length]['class'] = $method->class;
  87. $this->_wordData[$this->_length]['name'] = $method->name;
  88. $this->_wordData[$this->_length]['auto'] = ['控制权限', 'protected'];
  89. $this->_length++;
  90. }
  91. }
  92. /**
  93. * 解析成员函数对应的注释内容
  94. */
  95. private function getNotes() {
  96. foreach ($this->_wordData as $key=>$val) {
  97. # 获得成员函数对应的注释内容
  98. $note = $val['obj']->getDocComment();
  99. # 删除头尾标记(这里的第三个换行删除很重要)
  100. $note = str_replace(['/**', '*/', '
  101. '], '', $note);
  102. $note = explode(' *', $note);
  103. # 删除为空数组并更新键名索引
  104. $note = array_values(array_filter($note));
  105. foreach ($note as $k=>$v) {
  106. foreach ($this->_wordGroup as $kk=>$vv) {
  107. if (strrpos($v, $kk) !== false) {
  108. unset($this->_wordData[$key]['obj']); // 删除操作对象
  109. $name = str_replace('@', '', $kk);
  110. $this->_wordData[$key][$name][0] = $vv;
  111. $word = str_replace(" $kk ", '', $v);
  112. # 对一些标记元做特殊处理
  113. if (strrpos($v, '@param') !== false) {
  114. $array = explode(' ', $word);
  115. $this->_wordData[$key][$name][1][$k] = $array;
  116. } else {
  117. $this->_wordData[$key][$name][1] = $word;
  118. }
  119. } else {
  120. if ($k == 0) {
  121. $this->_wordData[$key]['title'][0] = '标题';
  122. $this->_wordData[$key]['title'][1] = $v;
  123. } else if ($k == 1) {
  124. $this->_wordData[$key]['des'][0] = '详情';
  125. if (strrpos($v, '@') === false) {
  126. $this->_wordData[$key]['des'][1] = $v;
  127. } else {
  128. $this->_wordData[$key]['des'][1] = '';
  129. }
  130. }
  131. }
  132. }
  133. }
  134. }
  135. }
  136. /**
  137. * 生成文档
  138. */
  139. public function makeWord() {
  140. $table = "<br/>函数规范:<hr/><table><tr><td width='150'>标记元</td><td width='120'>说明</td><td>详细</td></tr>";
  141. $html = '';
  142. foreach ($this->_wordData as $key=>$val) {
  143. foreach ($val as $k=>$v) {
  144. if ($k == 'class') {
  145. $html .= "<h2>函数路径:$v -> ";
  146. } else if ($k == 'name') {
  147. $html .= "$v();</h2><hr/>";
  148. } else if ($k == 'auto') {
  149. $html .= '<h3>'.$v[0].':'.$v[1].'<h3/>';
  150. } else if ($k == 'title') {
  151. $html .= '<h3>'.$v[0].':'.$v[1].'<h3/>';
  152. if (empty($val['des'])) {
  153. $html .= $table;
  154. }
  155. } else if ($k == 'des') {
  156. $html .= '<h3>'.$v[0].':'.$v[1].'<h3/>'.$table;
  157. } else if ($k != 'param') {
  158. $html .= '<tr><td>@'.$k.'</td><td>'.$v[0].'</td><td>'.$v[1].'</td></tr>';
  159. }
  160. }
  161. $html .= '</table>';
  162. if (!empty($val['param'])) {
  163. $html .= "<br/>函数参数:<hr/><table><tr><td width='150'>变量名</td><td width='120'>类型</td><td>说明</td></tr>";
  164. foreach ($val['param'][1] as $k=>$v) {
  165. $html .= '<tr><td>'.$v[1].'</td><td>'.$v[0].'</td><td>'.$v[2].'</td></tr>';
  166. }
  167. $html .= '</table><hr/><br/>';
  168. }
  169. }
  170. echo $html;
  171. }
  172. }
  173. # 使用demo
  174. $obj = new WordApi('TestA');
  175. $obj->makeWord();