├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── UPGRADING.md ├── composer.json ├── composer.lock └── src ├── ApiDocServiceProvider.php ├── Http └── Controllers │ └── GrawebApiDocController.php ├── Resources └── views │ └── index.blade.php └── Routes └── web.php /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | Todas as mudanças notáveis ​​neste projeto serão documentadas neste arquivo. 4 | 5 | Este projeto adere ao [Versão Semântica](https://semver.org/spec/v2.0.0.html), e o formato deste changelog é baseado em [Manter um Changelog](https://keepachangelog.com/en/1.0.0/). 6 | 7 | Mudanças significativas são marcadas com ⚠️. -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribuindo 2 | 3 | Obrigado pelo seu interesse em contribuir com o ApiDoc! Contribuições são bem-vindas e serão creditadas. 4 | 5 | Para manter as coisas funcionando sem problemas, pedimos que você siga algumas diretrizes ao contribuir. Leia e entenda este guia de contribuição antes de criar um problema ou solicitação de pull. 6 | 7 | ## Etiqueta 8 | 9 | Seja educado e humilde. 10 | 11 | ## Viabilidade 12 | 13 | Se você tem uma ideia para um recurso, preferimos que você abra uma discussão antes de se dar ao trabalho de escrever código. Suas ideias são bem-vindas, mas gostaríamos de trabalhar com você para criar soluções que funcionem bem para o projeto como um todo. Geralmente somos muito receptivos, então não deve demorar muito para descobrirmos se e como implementar melhor sua ideia. 14 | 15 | ## Procedimento 16 | 17 | Antes de registrar um problema: 18 | 19 | - Tente replicar o problema, para garantir que não foi uma coincidência. 20 | - Verifique se sua sugestão de recurso ainda não está presente no projeto. 21 | - Verifique a guia de solicitações de pull para garantir que seu recurso ou correção de bug ainda não esteja em andamento. 22 | 23 | Antes de enviar uma solicitação de pull: 24 | 25 | - Verifique a base de código para garantir que seu recurso ainda não exista. 26 | - Verifique as solicitações de pull para garantir que outra pessoa ainda não tenha enviado o recurso ou correção. 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Tighten Co. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Instalação 2 | 3 | ### Tente com 4 | - composer require graweb/apidoc 5 | 6 | ### Se não funcionar, tente com 7 | - composer require graweb/apidoc:*@dev 8 | 9 | ## Instruções 10 | 11 | 1 - Coloque o modelo de comentário em cada função do controller 12 | ``` 13 | /** 14 | * @title Faz o login na plataforma 15 | * @param [string] $email E-mail do usuário (obrigatório) 16 | * @param [string] $password Senha do usuário (obrigatório) 17 | * @param ... 18 | * @success 200 19 | * @erro 404 20 | */ 21 | ``` 22 | 2 - ```localhost/apidoc``` 23 | -------------------------------------------------------------------------------- /UPGRADING.md: -------------------------------------------------------------------------------- 1 | # Upgrade Guide 2 | 3 | ## First version 4 | 5 | - 1.0.0 -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "graweb/apidoc", 3 | "version": "1.0.0", 4 | "description": "API Documentation", 5 | "keywords": [ 6 | "laravel", 7 | "routes", 8 | "apidoc", 9 | "graweb" 10 | ], 11 | "type": "library", 12 | "license": "MIT", 13 | "homepage": "https://github.com/graweb/apidoc", 14 | "autoload": { 15 | "psr-4": { 16 | "Graweb\\Apidoc\\": "src/" 17 | } 18 | }, 19 | "authors": [ 20 | { 21 | "name": "Gustavo Grativol", 22 | "email": "graweb1@gmail.com", 23 | "homepage": "https://github.com/graweb", 24 | "role": "Developer" 25 | } 26 | ], 27 | "require": { 28 | "php": ">=8.1", 29 | "laravel/framework": ">=9.0" 30 | }, 31 | "extra": { 32 | "laravel": { 33 | "providers": [ 34 | "Graweb\\Apidoc\\ApiDocServiceProvider" 35 | ] 36 | } 37 | }, 38 | "minimum-stability": "stable", 39 | "prefer-stable": true 40 | } 41 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "c77eec3d54ad647b679e3ff5dea09e2d", 8 | "packages": [], 9 | "packages-dev": [], 10 | "aliases": [], 11 | "minimum-stability": "stable", 12 | "stability-flags": [], 13 | "prefer-stable": false, 14 | "prefer-lowest": false, 15 | "platform": [], 16 | "platform-dev": [], 17 | "plugin-api-version": "2.6.0" 18 | } 19 | -------------------------------------------------------------------------------- /src/ApiDocServiceProvider.php: -------------------------------------------------------------------------------- 1 | loadRoutesFrom(__DIR__.'/Routes/web.php'); 17 | $this->loadViewsFrom(__DIR__.'/Resources/views', 'apidoc'); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Http/Controllers/GrawebApiDocController.php: -------------------------------------------------------------------------------- 1 | uri)[1] ?? 'default'; 22 | 23 | $action = $route->getActionName(); 24 | 25 | // Verificar se a ação do route é válida (controller@method) 26 | if (str_contains($action, '@')) { 27 | [$controller, $method] = explode('@', $action); 28 | 29 | try { 30 | // Usar Reflection para pegar os comentários (docblock) 31 | $reflection = new ReflectionClass($controller); 32 | 33 | if ($reflection->hasMethod($method)) { 34 | $reflectionMethod = $reflection->getMethod($method); 35 | 36 | // Obter o comentário do método 37 | $docComment = $reflectionMethod->getDocComment(); 38 | 39 | // Limpar o comentário, removendo os caracteres indesejados 40 | $docComment = $this->cleanDocComment($docComment); 41 | 42 | // Se o docComment for nulo, atribua uma string vazia para evitar erros 43 | $docComment = $docComment ?? ''; 44 | 45 | // Obter os comentários dos parâmetros 46 | $paramsComments = $this->getParamComments($reflectionMethod); 47 | 48 | // Verificar se o docComment não é nulo antes de tentar obter o comentário do retorno 49 | $returnComment = $docComment ? $this->getReturnComment($docComment) : null; 50 | 51 | $title = $this->getTitle($reflectionMethod); 52 | $error = $this->getError($reflectionMethod); 53 | $success = $this->getSuccess($reflectionMethod); 54 | } else { 55 | $commentDetails = $paramsComments = $returnComment = null; 56 | } 57 | } catch (\ReflectionException $e) { 58 | $commentDetails = $paramsComments = $returnComment = null; 59 | } 60 | } else { 61 | $commentDetails = $paramsComments = $returnComment = null; 62 | } 63 | 64 | $routes[$prefix][] = [ 65 | 'title' => $title, 66 | 'erro' => $error, 67 | 'success' => $success, 68 | 'uri' => $route->uri, 69 | 'method' => $route->methods(), 70 | 'name' => $route->getName(), 71 | 'action' => $route->getActionName(), 72 | 'params_comments' => $paramsComments, 73 | 'return_comment' => $returnComment 74 | ]; 75 | } 76 | 77 | unset($routes['default'], $routes['{endpoint}'], $routes['']); 78 | return view('apidoc::index', compact('routes')); 79 | } 80 | 81 | /** 82 | * Extrai os comentários dos parâmetros do método. 83 | * 84 | * @param ReflectionMethod $method 85 | * @return array 86 | */ 87 | private function getTitle(ReflectionMethod $method): string 88 | { 89 | $title = "Não encontrado"; 90 | $docComment = $method->getDocComment(); 91 | preg_match('/@title\s+([^\n]+)/', $docComment, $matches); 92 | if (isset($matches[1])) { 93 | $title = $matches[1]; 94 | } 95 | 96 | return $title; 97 | } 98 | 99 | /** 100 | * Extrai os comentários dos parâmetros do método. 101 | * 102 | * @param ReflectionMethod $method 103 | * @return array 104 | */ 105 | private function getError(ReflectionMethod $method): string 106 | { 107 | $error = "Não encontrado"; 108 | $docComment = $method->getDocComment(); 109 | preg_match('/@erro\s+([^\n]+)/', $docComment, $matches); 110 | if (isset($matches[1])) { 111 | $error = $matches[1]; 112 | } 113 | 114 | return $error; 115 | } 116 | 117 | /** 118 | * Extrai os comentários dos parâmetros do método. 119 | * 120 | * @param ReflectionMethod $method 121 | * @return array 122 | */ 123 | private function getSuccess(ReflectionMethod $method): string 124 | { 125 | $success = "Não encontrado"; 126 | $docComment = $method->getDocComment(); 127 | preg_match('/@success\s+([^\n]+)/', $docComment, $matches); 128 | if (isset($matches[1])) { 129 | $success = $matches[1]; 130 | } 131 | 132 | return $success; 133 | } 134 | 135 | /** 136 | * Extrai os comentários dos parâmetros do método. 137 | * 138 | * @param ReflectionMethod $method 139 | * @return array 140 | */ 141 | private function getParamComments(ReflectionMethod $method): array 142 | { 143 | $paramsComments = []; 144 | 145 | // Obtém os comentários da função/método 146 | $docComment = $method->getDocComment(); 147 | 148 | // Processa o comentário do DocBlock para capturar todos os parâmetros 149 | if ($docComment) { 150 | preg_match_all('/@param\s+\[([^\]]+)]\s+\$(\w+)\s+(.+)/', $docComment, $matches, PREG_SET_ORDER); 151 | 152 | foreach ($matches as $match) { 153 | // Adiciona os detalhes do parâmetro 154 | $paramsComments[] = [ 155 | 'name' => $match[2], 156 | 'type' => $match[1], 157 | 'description' => trim($match[3]), 158 | ]; 159 | } 160 | } 161 | 162 | return $paramsComments; 163 | } 164 | 165 | /** 166 | * Extrai o comentário do retorno do método. 167 | * 168 | * @param string $docComment 169 | * @return string|null 170 | */ 171 | private function getReturnComment(string $docComment): ?string 172 | { 173 | preg_match('/@return\s+([^\s]+)/', $docComment, $matches); 174 | 175 | return $matches[1] ?? null; 176 | } 177 | 178 | /** 179 | * Limpa o docblock para retornar apenas o texto. 180 | * 181 | * @param string|null $docComment 182 | * @return string|null 183 | */ 184 | private function cleanDocComment(?string $docComment): ?string 185 | { 186 | if (!$docComment) { 187 | return null; 188 | } 189 | 190 | // Remove os delimitadores do docblock e os asteriscos 191 | $lines = explode("\n", $docComment); 192 | $lines = array_map(function ($line) { 193 | $line = trim($line); 194 | return preg_replace('/^[\/\*\s]+|[\*\s]+$/', '', $line); // Remove "/*", "*", "*/" e espaços extras 195 | }, $lines); 196 | 197 | // Junta as linhas restantes, removendo as vazias 198 | $cleaned = implode(' ', array_filter($lines)); 199 | 200 | return trim($cleaned); 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/Resources/views/index.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{ config('app.name', 'Laravel') }} - Documentação da API 8 | 9 | 10 | 11 | 12 | 13 | 65 | 66 | 67 |
68 |
69 |

Instruções

70 | 79 |
80 |
81 |
82 |

Aplique o modelo de comentário abaixo em cada função do controller

83 |

Se o modelo abaixo estiver em branco ou fora do padrão, o retorno vai ser "Não encontrado"

84 |
85 |

/**

86 |

* @title Ação que o código realiza

87 |

* @param [string] $email Parâmetro e-mail do usuário (obrigatório)

88 |

* @param [string] $password Parâmetro senha do usuário (obrigatório)

89 |

* @param ...

90 |

* @success 200

91 |

* @erro 404

92 |

*/

93 |
94 |
95 | 96 | @foreach ($routes as $group => $groupRoutes) 97 |
98 |

{{ ucfirst($group) }}

99 |
    100 | @foreach ($groupRoutes as $route) 101 |
  • 102 |

    Ação: {{ $route['title'] }}

    103 |

    Requisição: {{ $route['uri'] }}

    104 |

    105 | Métodos: 106 | @foreach ($route['method'] as $method) 107 | 114 | {{ $method }} 115 | 116 | @endforeach 117 |

    118 |

    Arquivo: {{ $route['action'] }}

    119 | 120 | @if (!empty($route['comment']) && is_string($route['comment'])) 121 |

    Descrição: {{ $route['comment'] }}

    122 | @endif 123 | 124 | @if (!empty($route['params_comments']) && is_array($route['params_comments'])) 125 |

    Parâmetros:

    126 |
      127 | @foreach ($route['params_comments'] as $param) 128 |
    • 129 | {{ $param['name'] . ' (' . $param['type'] . ') - ' . $param['description'] }} 130 |
    • 131 | @endforeach 132 |
    133 | @endif 134 | 135 | @if (!empty($route['return_comment']) && is_string($route['return_comment'])) 136 |

    Retorno: {{ $route['return_comment'] }}

    137 | @endif 138 | 139 |

    140 | Sucesso: 141 | @if ($route['success'] == '201') 142 | {{ $route['success'] }} 143 | @else 144 | {{ $route['success'] }} 145 | @endif 146 |

    147 |

    148 | Erro: 149 | @if ($route['erro'] == '404') 150 | {{ $route['erro'] }} 151 | @else 152 | {{ $route['erro'] }} 153 | @endif 154 |

    155 |
  • 156 | @endforeach 157 |
158 |
159 | @endforeach 160 |
161 |
162 | 163 | 230 | 231 | 232 | 233 | -------------------------------------------------------------------------------- /src/Routes/web.php: -------------------------------------------------------------------------------- 1 | group(function () { 7 | Route::get('/', [GrawebApiDocController::class, 'index']); 8 | }); 9 | --------------------------------------------------------------------------------