getDefaultConfig('get_path'); $apiLists = $this->scan($getPath); return [ "title" => $appName . 'API 接口文档', "version" => '', 'host' => Tool::url(Hy::request()->url())->getDomain(), 'apiLists' => $apiLists, ]; } /** * @param string|null $key * * @return array|mixed */ private function getDefaultConfig(string $key = null): mixed { if (!$this->config) { $this->config = Hy::config('plugins.ApiDoc') ?: include __DIR__ . '/../config.php'; } return $key === null ? $this->config : $this->config[$key]; } /** * @param array $paths * * @return array * @throws \Exception */ private function scan(array $paths): array { $apiList = []; foreach ($paths as ['path' => $path, 'namespace' => $namespace]) { Tool::dir($path)->each(function (Tool\Dir\EachFile $eachFile) use ($namespace, &$apiList){ $classname = strtr(basename($eachFile->filename), ['.php' => '']); $classFullName = implode("\\", [$namespace, ...$eachFile->relativelyDirs, $classname]); try { $class = \Hyperf\Support\make($classFullName); if (!$class) return; $reflectionClass = $this->apiClassCheck($class); if ($reflectionClass) { $apiList[] = $this->apiResolve($reflectionClass); } } catch (\Throwable $throwable) {} }); } return $apiList; } /** * @param $class * * @return \ReflectionClass|null * @throws \ReflectionException */ private function apiClassCheck($class): ?\ReflectionClass { $reflexClass = new \ReflectionClass($class); if (!$reflexClass->getAttributes(Api::class) || !$reflexClass->getAttributes(Controller::class)) { return null; } return $reflexClass; } /** * @param \ReflectionClass $reflectionClass * * @return array */ private function apiResolve(\ReflectionClass $reflectionClass): array { $authenticateType = $this->authenticateType($reflectionClass); $groupTitle = $this->apiTitleResolve($reflectionClass->getDocComment()); $baseUri = $reflectionClass->getAttributes(Controller::class)[0]->newInstance()->prefix; $apis = []; foreach ($reflectionClass->getMethods() as $reflectionMethod) { if ($api = $this->methodResolve($reflectionMethod)) { $api['url'] = "/" . $baseUri . '/' . $api['url']; $api['auth'] = $api['auth'] ?: $authenticateType; $apis[] = $api; } } return [ 'name' => $groupTitle, 'children' => $apis, ]; } /** * @param string $comment * * @return string */ private function apiTitleResolve(string $comment): string { preg_match('/^\/(\*|\s)+([^\*\s]+)/', $comment, $document); return empty($document[2]) ? "未命名" : $document[2]; } /** * @param \ReflectionClass|\ReflectionMethod $reflection * * @return string|null */ private function authenticateType(\ReflectionClass|\ReflectionMethod $reflection): ?string { $type = null; $Api = $reflection->getAttributes(Api::class); $Middlewares = $reflection->getAttributes(Middlewares::class); $Middleware = $reflection->getAttributes(Middleware::class); $haveMiddlewares = $Middlewares ? ($Middlewares[0]->newInstance()->middlewares ?: []) : []; $Middleware and $haveMiddlewares = [...$haveMiddlewares, ...array_map(fn($attribute) => $attribute->newInstance(), $Middleware)]; if ($haveMiddlewares){ foreach ($haveMiddlewares as $middleware) { if ($middleware->middleware === ApiAuthenticateMiddleware::class) { $type = "strength"; break; } } }else { if ($Api[0]->newInstance()->isAuthenticate){ $type = "weak"; } } return $type; } /** * @param \ReflectionMethod $reflectionMethod * * @return array|null */ private function methodResolve(\ReflectionMethod $reflectionMethod): ?array { if (!$reflectionMethod->getAttributes(Api::class)) { return null; } $api = [ 'name' => $this->apiTitleResolve($reflectionMethod->getDocComment()), 'call' => $this->getCallLocation($reflectionMethod), 'url' => '', 'method' => 'GET', 'version' => [], 'auth' => $this->authenticateType($reflectionMethod), 'requestParams' => [], 'responseParams' => [], ]; foreach ($reflectionMethod->getAttributes() as $attribute) { $attribute = $attribute->newInstance(); switch (true) { case $attribute instanceof PostMapping: $api['method'] = 'POST'; case $attribute instanceof Mapping: $api['url'] = $attribute->path; break; case $attribute instanceof ApiQuery: case $attribute instanceof ApiBody: $api['requestParams'][] = json_decode(json_encode($attribute), true); break; case $attribute instanceof ApiReturn: $api['responseParams'][] = json_decode(json_encode($attribute), true); break; case $attribute instanceof ApiVersion: $api['version'][] = $attribute->version; } } $api['responseParams'] = $this->childrenParamHandle($api['responseParams']); if ($api['method'] === 'POST') { $api['requestParams'] = $this->childrenParamHandle($api['requestParams']); } return $api; } /** * @param array $initialData * * @return array */ private function childrenParamHandle(array $initialData): array { $newData = []; foreach ($initialData as $item) { $item['children'] = []; $names = explode('.', $item['name']); $current = &$newData; foreach (array_slice($names, 0, -1) as $attr) { $current = &$current[$attr]['children']; } $item['name'] = end($names); $current[$item['name']] = $item; } return $this->childrenData(array_values($newData)); } /** * @param array $data * * @return array */ private function childrenData(array $data): array { return array_map(function ($value){ if ($value['children']){ $value['children'] = array_values($this->childrenData($value['children'])); } return $value; }, $data); } /** * @param \ReflectionMethod $method * * @return string */ private function getCallLocation(\ReflectionMethod $method): string { return $method->getDeclaringClass()->getNamespaceName() . "\\" . $method->getDeclaringClass()->getShortName() . "@" . $method->getName(); } }