controllersPath = $controllersPath; $this->modelsPath = $modelsPath; } public function generateDocumentation(): array { $docs = [ 'title' => 'Media API Documentation', 'version' => '1.0.0', 'baseUrl' => '/api', 'endpoints' => [], 'models' => [] ]; // Controller scannen $docs['endpoints'] = $this->scanControllers(); // Modelle scannen $docs['models'] = $this->scanModels(); return $docs; } private function scanControllers(): array { $endpoints = []; $controllerFiles = glob($this->controllersPath . '*Controller.php'); foreach ($controllerFiles as $file) { $className = basename($file, '.php'); require_once $file; $reflection = new ReflectionClass($className); $methods = $reflection->getMethods(ReflectionMethod::IS_PUBLIC); foreach ($methods as $method) { if ($method->getName() === '__construct') { continue; } $docComment = $method->getDocComment(); $endpointInfo = $this->parseMethodDoc($docComment, $method->getName(), $className); if ($endpointInfo) { $endpoints[] = $endpointInfo; } } } return $endpoints; } private function parseMethodDoc(?string $docComment, string $methodName, string $className): ?array { if (!$docComment) { return null; } $info = [ 'controller' => $className, 'method' => $methodName, 'description' => '', 'parameters' => [], 'response' => [], 'example' => null ]; // Beschreibung extrahieren if (preg_match('/\*\s*(.+?)\n/', $docComment, $matches)) { $info['description'] = trim($matches[1]); } // @param extrahieren if (preg_match_all('/@param\s+(\w+)\s+\$(\w+)\s+(.+)/', $docComment, $matches)) { foreach ($matches[1] as $i => $type) { $info['parameters'][] = [ 'name' => $matches[2][$i], 'type' => $type, 'description' => trim($matches[3][$i]) ]; } } // @return extrahieren if (preg_match('/@return\s+(\w+)\s+(.+)/', $docComment, $matches)) { $info['response'] = [ 'type' => $matches[1], 'description' => trim($matches[2]) ]; } // @example extrahieren if (preg_match('/@example\s+(.+)/', $docComment, $matches)) { $info['example'] = trim($matches[1]); } // HTTP-Methoden und Pfade aus Methodennamen ableiten $info['httpMethods'] = $this->inferHttpMethods($methodName); $info['path'] = $this->inferPath($className, $methodName); return $info; } private function inferHttpMethods(string $methodName): array { $methods = []; if (strpos($methodName, 'get') === 0) { $methods[] = 'GET'; } if (strpos($methodName, 'create') !== false || strpos($methodName, 'add') !== false || strpos($methodName, 'post') !== false) { $methods[] = 'POST'; } if (strpos($methodName, 'update') !== false) { $methods[] = 'PUT'; } if (strpos($methodName, 'delete') !== false) { $methods[] = 'DELETE'; } if (empty($methods)) { $methods[] = 'GET'; // Default } return $methods; } private function inferPath(string $className, string $methodName): string { $resource = strtolower(str_replace('Controller', '', $className)); $path = "/{$resource}"; if (strpos($methodName, 'getOne') !== false || strpos($methodName, 'update') !== false || strpos($methodName, 'delete') !== false) { $path .= '/:id'; } if (strpos($methodName, 'getMedia') !== false) { $path .= '/:id/media'; } if (strpos($methodName, 'handleEpisodes') !== false) { $path .= '/:id/episodes'; } if (strpos($methodName, 'handleTracks') !== false) { $path .= '/:id/tracks'; } if (strpos($methodName, 'handleAdult') !== false) { $path .= '/adult'; } return $path; } private function scanModels(): array { $models = []; $modelFiles = glob($this->modelsPath . '*.php'); foreach ($modelFiles as $file) { $className = basename($file, '.php'); if ($className === 'BaseModel' || $className === 'MediaType') { continue; } require_once $file; $reflection = new ReflectionClass($className); $docComment = $reflection->getDocComment(); $modelInfo = [ 'name' => $className, 'description' => '', 'fields' => [] ]; if ($docComment) { if (preg_match('/\*\s*(.+?)\n/', $docComment, $matches)) { $modelInfo['description'] = trim($matches[1]); } } // Properties aus Klasse ableiten $properties = $reflection->getProperties(); foreach ($properties as $property) { if ($property->isPublic() || $property->isProtected()) { $modelInfo['fields'][] = [ 'name' => $property->getName(), 'type' => 'mixed', 'description' => '' ]; } } $models[] = $modelInfo; } return $models; } }