You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

180 lines
6.9 KiB

  1. <?php
  2. namespace app\admin\command;
  3. use app\admin\command\Api\library\Builder;
  4. use think\Config;
  5. use think\console\Command;
  6. use think\console\Input;
  7. use think\console\input\Option;
  8. use think\console\Output;
  9. use think\Exception;
  10. class Api extends Command
  11. {
  12. protected function configure()
  13. {
  14. $site = Config::get('site');
  15. $this
  16. ->setName('api')
  17. ->addOption('url', 'u', Option::VALUE_OPTIONAL, 'default api url', '')
  18. ->addOption('module', 'm', Option::VALUE_OPTIONAL, 'module name(admin/index/api)', 'api')
  19. ->addOption('output', 'o', Option::VALUE_OPTIONAL, 'output index file name', 'api.html')
  20. ->addOption('template', 'e', Option::VALUE_OPTIONAL, '', 'index.html')
  21. ->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override general file', false)
  22. ->addOption('title', 't', Option::VALUE_OPTIONAL, 'document title', $site['name'])
  23. ->addOption('author', 'a', Option::VALUE_OPTIONAL, 'document author', $site['name'])
  24. ->addOption('class', 'c', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'extend class', null)
  25. ->addOption('language', 'l', Option::VALUE_OPTIONAL, 'language', 'zh-cn')
  26. ->setDescription('Build Api document from controller');
  27. }
  28. protected function execute(Input $input, Output $output)
  29. {
  30. $apiDir = __DIR__ . DS . 'Api' . DS;
  31. $force = $input->getOption('force');
  32. $url = $input->getOption('url');
  33. $language = $input->getOption('language');
  34. $language = $language ? $language : 'zh-cn';
  35. $langFile = $apiDir . 'lang' . DS . $language . '.php';
  36. if (!is_file($langFile)) {
  37. throw new Exception('language file not found');
  38. }
  39. $lang = include_once $langFile;
  40. // 目标目录
  41. $output_dir = ROOT_PATH . 'public' . DS;
  42. $output_file = $output_dir . $input->getOption('output');
  43. if (is_file($output_file) && !$force) {
  44. throw new Exception("api index file already exists!\nIf you need to rebuild again, use the parameter --force=true ");
  45. }
  46. // 模板文件
  47. $template_dir = $apiDir . 'template' . DS;
  48. $template_file = $template_dir . $input->getOption('template');
  49. if (!is_file($template_file)) {
  50. throw new Exception('template file not found');
  51. }
  52. // 额外的类
  53. $classes = $input->getOption('class');
  54. // 标题
  55. $title = $input->getOption('title');
  56. // 作者
  57. $author = $input->getOption('author');
  58. // 模块
  59. $module = $input->getOption('module');
  60. $moduleDir = APP_PATH . $module . DS;
  61. if (!is_dir($moduleDir)) {
  62. throw new Exception('module not found');
  63. }
  64. if (version_compare(PHP_VERSION, '7.0.0', '<')) {
  65. if (extension_loaded('Zend OPcache')) {
  66. $configuration = opcache_get_configuration();
  67. $directives = $configuration['directives'];
  68. $configName = request()->isCli() ? 'opcache.enable_cli' : 'opcache.enable';
  69. if (!$directives[$configName]) {
  70. throw new Exception("Please make sure {$configName} is turned on, Get help:https://forum.fastadmin.net/d/1321");
  71. }
  72. } else {
  73. throw new Exception("Please make sure opcache already enabled, Get help:https://forum.fastadmin.net/d/1321");
  74. }
  75. }
  76. $controllerDir = $moduleDir . Config::get('url_controller_layer') . DS;
  77. $files = new \RecursiveIteratorIterator(
  78. new \RecursiveDirectoryIterator($controllerDir),
  79. \RecursiveIteratorIterator::LEAVES_ONLY
  80. );
  81. foreach ($files as $name => $file) {
  82. if (!$file->isDir() && $file->getExtension() == 'php') {
  83. $filePath = $file->getRealPath();
  84. $classes[] = $this->get_class_from_file($filePath);
  85. }
  86. }
  87. $classes = array_unique(array_filter($classes));
  88. $config = [
  89. 'sitename' => config('site.name'),
  90. 'title' => $title,
  91. 'author' => $author,
  92. 'description' => '',
  93. 'apiurl' => $url,
  94. 'language' => $language,
  95. ];
  96. $builder = new Builder($classes);
  97. $content = $builder->render($template_file, ['config' => $config, 'lang' => $lang]);
  98. if (!file_put_contents($output_file, $content)) {
  99. throw new Exception('Cannot save the content to ' . $output_file);
  100. }
  101. $output->info("Build Successed!");
  102. }
  103. /**
  104. * get full qualified class name
  105. *
  106. * @param string $path_to_file
  107. * @return string
  108. * @author JBYRNE http://jarretbyrne.com/2015/06/197/
  109. */
  110. protected function get_class_from_file($path_to_file)
  111. {
  112. //Grab the contents of the file
  113. $contents = file_get_contents($path_to_file);
  114. //Start with a blank namespace and class
  115. $namespace = $class = "";
  116. //Set helper values to know that we have found the namespace/class token and need to collect the string values after them
  117. $getting_namespace = $getting_class = false;
  118. //Go through each token and evaluate it as necessary
  119. foreach (token_get_all($contents) as $token) {
  120. //If this token is the namespace declaring, then flag that the next tokens will be the namespace name
  121. if (is_array($token) && $token[0] == T_NAMESPACE) {
  122. $getting_namespace = true;
  123. }
  124. //If this token is the class declaring, then flag that the next tokens will be the class name
  125. if (is_array($token) && $token[0] == T_CLASS) {
  126. $getting_class = true;
  127. }
  128. //While we're grabbing the namespace name...
  129. if ($getting_namespace === true) {
  130. //If the token is a string or the namespace separator...
  131. if (is_array($token) && in_array($token[0], [T_STRING, T_NS_SEPARATOR])) {
  132. //Append the token's value to the name of the namespace
  133. $namespace .= $token[1];
  134. } elseif ($token === ';') {
  135. //If the token is the semicolon, then we're done with the namespace declaration
  136. $getting_namespace = false;
  137. }
  138. }
  139. //While we're grabbing the class name...
  140. if ($getting_class === true) {
  141. //If the token is a string, it's the name of the class
  142. if (is_array($token) && $token[0] == T_STRING) {
  143. //Store the token's value as the class name
  144. $class = $token[1];
  145. //Got what we need, stope here
  146. break;
  147. }
  148. }
  149. }
  150. //Build the fully-qualified class name and return it
  151. return $namespace ? $namespace . '\\' . $class : $class;
  152. }
  153. }