190 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
		
		
			
		
	
	
			190 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| 
								 | 
							
								<?php
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								namespace app\admin\command;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								use app\admin\command\Api\library\Builder;
							 | 
						||
| 
								 | 
							
								use think\Config;
							 | 
						||
| 
								 | 
							
								use think\console\Command;
							 | 
						||
| 
								 | 
							
								use think\console\Input;
							 | 
						||
| 
								 | 
							
								use think\console\input\Option;
							 | 
						||
| 
								 | 
							
								use think\console\Output;
							 | 
						||
| 
								 | 
							
								use think\Exception;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class Api extends Command
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    protected function configure()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        $site = Config::get('site');
							 | 
						||
| 
								 | 
							
								        $this
							 | 
						||
| 
								 | 
							
								            ->setName('api')
							 | 
						||
| 
								 | 
							
								            ->addOption('url', 'u', Option::VALUE_OPTIONAL, 'default api url', '')
							 | 
						||
| 
								 | 
							
								            ->addOption('module', 'm', Option::VALUE_OPTIONAL, 'module name(admin/index/api)', 'api')
							 | 
						||
| 
								 | 
							
								            ->addOption('output', 'o', Option::VALUE_OPTIONAL, 'output index file name', 'api.html')
							 | 
						||
| 
								 | 
							
								            ->addOption('template', 'e', Option::VALUE_OPTIONAL, '', 'index.html')
							 | 
						||
| 
								 | 
							
								            ->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override general file', false)
							 | 
						||
| 
								 | 
							
								            ->addOption('title', 't', Option::VALUE_OPTIONAL, 'document title', $site['name'] ?? '')
							 | 
						||
| 
								 | 
							
								            ->addOption('class', 'c', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'extend class', null)
							 | 
						||
| 
								 | 
							
								            ->addOption('language', 'l', Option::VALUE_OPTIONAL, 'language', 'zh-cn')
							 | 
						||
| 
								 | 
							
								            ->addOption('addon', 'a', Option::VALUE_OPTIONAL, 'addon name', null)
							 | 
						||
| 
								 | 
							
								            ->addOption('controller', 'r', Option::VALUE_REQUIRED | Option::VALUE_IS_ARRAY, 'controller name', null)
							 | 
						||
| 
								 | 
							
								            ->setDescription('Build Api document from controller');
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    protected function execute(Input $input, Output $output)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        $apiDir = __DIR__ . DS . 'Api' . DS;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        $force = $input->getOption('force');
							 | 
						||
| 
								 | 
							
								        $url = $input->getOption('url');
							 | 
						||
| 
								 | 
							
								        $language = $input->getOption('language');
							 | 
						||
| 
								 | 
							
								        $template = $input->getOption('template');
							 | 
						||
| 
								 | 
							
								        if (!preg_match("/^([a-z0-9]+)\.html\$/i", $template)) {
							 | 
						||
| 
								 | 
							
								            throw new Exception('template file not correct');
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        $language = $language ? $language : 'zh-cn';
							 | 
						||
| 
								 | 
							
								        $langFile = $apiDir . 'lang' . DS . $language . '.php';
							 | 
						||
| 
								 | 
							
								        if (!is_file($langFile)) {
							 | 
						||
| 
								 | 
							
								            throw new Exception('language file not found');
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        $lang = include_once $langFile;
							 | 
						||
| 
								 | 
							
								        // 目标目录
							 | 
						||
| 
								 | 
							
								        $output_dir = ROOT_PATH . 'public' . DS;
							 | 
						||
| 
								 | 
							
								        $output_file = $output_dir . $input->getOption('output');
							 | 
						||
| 
								 | 
							
								        if (is_file($output_file) && !$force) {
							 | 
						||
| 
								 | 
							
								            throw new Exception("api index file already exists!\nIf you need to rebuild again, use the parameter --force=true ");
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        // 模板文件
							 | 
						||
| 
								 | 
							
								        $template_dir = $apiDir . 'template' . DS;
							 | 
						||
| 
								 | 
							
								        $template_file = $template_dir . $template;
							 | 
						||
| 
								 | 
							
								        if (!is_file($template_file)) {
							 | 
						||
| 
								 | 
							
								            throw new Exception('template file not found');
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        // 额外的类
							 | 
						||
| 
								 | 
							
								        $classes = $input->getOption('class');
							 | 
						||
| 
								 | 
							
								        // 标题
							 | 
						||
| 
								 | 
							
								        $title = $input->getOption('title');
							 | 
						||
| 
								 | 
							
								        // 模块
							 | 
						||
| 
								 | 
							
								        $module = $input->getOption('module');
							 | 
						||
| 
								 | 
							
								        // 插件
							 | 
						||
| 
								 | 
							
								        $addon = $input->getOption('addon');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        $moduleDir = $addonDir = '';
							 | 
						||
| 
								 | 
							
								        if ($addon) {
							 | 
						||
| 
								 | 
							
								            $addonInfo = get_addon_info($addon);
							 | 
						||
| 
								 | 
							
								            if (!$addonInfo) {
							 | 
						||
| 
								 | 
							
								                throw new Exception('addon not found');
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            $moduleDir = ADDON_PATH . $addon . DS;
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								            $moduleDir = APP_PATH . $module . DS;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (!is_dir($moduleDir)) {
							 | 
						||
| 
								 | 
							
								            throw new Exception('module not found');
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (version_compare(PHP_VERSION, '7.0.0', '<')) {
							 | 
						||
| 
								 | 
							
								            throw new Exception("Requires PHP version 7.0 or newer");
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        //控制器名
							 | 
						||
| 
								 | 
							
								        $controller = $input->getOption('controller') ?: [];
							 | 
						||
| 
								 | 
							
								        if (!$controller) {
							 | 
						||
| 
								 | 
							
								            $controllerDir = $moduleDir . Config::get('url_controller_layer') . DS;
							 | 
						||
| 
								 | 
							
								            $files = new \RecursiveIteratorIterator(
							 | 
						||
| 
								 | 
							
								                new \RecursiveDirectoryIterator($controllerDir),
							 | 
						||
| 
								 | 
							
								                \RecursiveIteratorIterator::LEAVES_ONLY
							 | 
						||
| 
								 | 
							
								            );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            foreach ($files as $name => $file) {
							 | 
						||
| 
								 | 
							
								                if (!$file->isDir() && $file->getExtension() == 'php') {
							 | 
						||
| 
								 | 
							
								                    $filePath = $file->getRealPath();
							 | 
						||
| 
								 | 
							
								                    $classes[] = $this->getClassFromFile($filePath);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								            foreach ($controller as $index => $item) {
							 | 
						||
| 
								 | 
							
								                $filePath = $moduleDir . Config::get('url_controller_layer') . DS . $item . '.php';
							 | 
						||
| 
								 | 
							
								                $classes[] = $this->getClassFromFile($filePath);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        $classes = array_unique(array_filter($classes));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        $config = [
							 | 
						||
| 
								 | 
							
								            'sitename'    => config('site.name'),
							 | 
						||
| 
								 | 
							
								            'title'       => $title,
							 | 
						||
| 
								 | 
							
								            'author'      => config('site.name'),
							 | 
						||
| 
								 | 
							
								            'description' => '',
							 | 
						||
| 
								 | 
							
								            'apiurl'      => $url,
							 | 
						||
| 
								 | 
							
								            'language'    => $language,
							 | 
						||
| 
								 | 
							
								        ];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        $builder = new Builder($classes);
							 | 
						||
| 
								 | 
							
								        $content = $builder->render($template_file, ['config' => $config, 'lang' => $lang]);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (!file_put_contents($output_file, $content)) {
							 | 
						||
| 
								 | 
							
								            throw new Exception('Cannot save the content to ' . $output_file);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        $output->info("Build Successed!");
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * 从文件获取命名空间和类名
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @param string $filename
							 | 
						||
| 
								 | 
							
								     * @return string
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    protected function getClassFromFile($filename)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        $getNext = null;
							 | 
						||
| 
								 | 
							
								        $isNamespace = false;
							 | 
						||
| 
								 | 
							
								        $skipNext = false;
							 | 
						||
| 
								 | 
							
								        $namespace = '';
							 | 
						||
| 
								 | 
							
								        $class = '';
							 | 
						||
| 
								 | 
							
								        foreach (\PhpToken::tokenize(file_get_contents($filename)) as $token) {
							 | 
						||
| 
								 | 
							
								            if (!$token->isIgnorable()) {
							 | 
						||
| 
								 | 
							
								                $name = $token->getTokenName();
							 | 
						||
| 
								 | 
							
								                switch ($name) {
							 | 
						||
| 
								 | 
							
								                    case 'T_NAMESPACE':
							 | 
						||
| 
								 | 
							
								                        $isNamespace = true;
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                    case 'T_EXTENDS':
							 | 
						||
| 
								 | 
							
								                    case 'T_USE':
							 | 
						||
| 
								 | 
							
								                    case 'T_IMPLEMENTS':
							 | 
						||
| 
								 | 
							
								                        $skipNext = true;
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                    case 'T_CLASS':
							 | 
						||
| 
								 | 
							
								                        if ($skipNext) {
							 | 
						||
| 
								 | 
							
								                            $skipNext = false;
							 | 
						||
| 
								 | 
							
								                        } else {
							 | 
						||
| 
								 | 
							
								                            $getNext = strtolower(substr($name, 2));
							 | 
						||
| 
								 | 
							
								                        }
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                    case 'T_NAME_QUALIFIED':
							 | 
						||
| 
								 | 
							
								                    case 'T_NS_SEPARATOR':
							 | 
						||
| 
								 | 
							
								                    case 'T_STRING':
							 | 
						||
| 
								 | 
							
								                    case ';':
							 | 
						||
| 
								 | 
							
								                        if ($isNamespace) {
							 | 
						||
| 
								 | 
							
								                            if ($name == ';') {
							 | 
						||
| 
								 | 
							
								                                $isNamespace = false;
							 | 
						||
| 
								 | 
							
								                            } else {
							 | 
						||
| 
								 | 
							
								                                $namespace .= $token->text;
							 | 
						||
| 
								 | 
							
								                            }
							 | 
						||
| 
								 | 
							
								                        } elseif ($skipNext) {
							 | 
						||
| 
								 | 
							
								                            $skipNext = false;
							 | 
						||
| 
								 | 
							
								                        } elseif ($getNext == 'class') {
							 | 
						||
| 
								 | 
							
								                            $class = $token->text;
							 | 
						||
| 
								 | 
							
								                            $getNext = null;
							 | 
						||
| 
								 | 
							
								                            break 2;
							 | 
						||
| 
								 | 
							
								                        }
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                    default:
							 | 
						||
| 
								 | 
							
								                        $getNext = null;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return $namespace . '\\' . $class;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 |