/** @return array */ public function generateRoutes() { $controllerFiles = array(); foreach ($this->controllerDirs as $dir) { $controllerFiles = array_merge($controllerFiles, FileList::getFilesRecursively($dir)); } $classes = array(); foreach ($controllerFiles as $file) { $content = file_get_contents($file); $namespace = ''; if (preg_match('/^namespace\\s+(\\S+);$/m', $content, $namespaceMatch)) { $namespace = '\\' . $namespaceMatch[1]; } if (!preg_match('/^class\\s+(\\S+)(?:(?:\\s+extends|\\s+implements|\\s*\\{)|$)/m', $content, $classMatch)) { continue; } $class = $classMatch[1]; $classes[] = $namespace . '\\' . $class; } $routes = array(); foreach ($classes as $class) { $rClass = new \ReflectionClass($class); $classPattern = ''; $classDefaults = array(); $classPriority = 0; foreach (AnnotationReader::getPhpdocTags($rClass->getDocComment()) as $annotation) { if ($annotation['name'] != 'route') { continue; } if (!empty($classPattern)) { throw new \Exception("Class {$class} has more than one @route annotation"); } $classPriority = $this->shiftPriorityFromArgs($annotation['args']); if (empty($annotation['args'])) { throw new \Exception("Class {$class} has incorrect @route annotation (syntax: @route [priority=123] /blah)"); } $classPattern = array_shift($annotation['args']); $classDefaults = $annotation['args']; } $rMethods = $rClass->getMethods(); foreach ($rMethods as $rMethod) { if ($rMethod->isAbstract() || !$rMethod->isPublic() || $rMethod->isStatic()) { continue; } foreach (AnnotationReader::getPhpdocTags($rMethod->getDocComment()) as $annotation) { if ($annotation['name'] != 'route') { continue; } // TODO файлы и строки/методы в тексты исключений $priority = $this->shiftPriorityFromArgs($annotation['args'], $classPriority); if (!empty($annotation['args']) && preg_match('/^priority=(-?\\d+)$/', $annotation['args'][0], $match)) { $priority = (int) $match[1]; array_shift($annotation['args']); } if (count($annotation['args']) < 2) { throw new \Exception('Incorrect annotation: ' . $annotation['string'] . ' (syntax: @route [priority=123] GET /blah)'); } $method = array_shift($annotation['args']); if (!preg_match('/^[A-Z]+$/', $method)) { throw new \Exception('Incorrect HTTP method in annotation: ' . $annotation['string']); } // class route "/blah" and method route "/" should give "/blah", not "/blah/" $pattern = rtrim($classPattern . array_shift($annotation['args']), '/'); $defaultDefs = array_merge($classDefaults, $annotation['args']); $defaults = array(); foreach ($defaultDefs as $def) { $parts = explode('=', $def, 2); if (count($parts) != 2) { throw new \Exception("Incorrect route default: {$def} in " . $annotation['string']); } $defaults[$parts[0]] = $parts[1]; } $routes[] = array('callback' => array($class, $rMethod->getName()), 'method' => $method, 'pattern' => $pattern, 'regexp' => static::makeRouteRegexp($pattern), 'defaults' => $defaults, 'priority' => $priority); } } } // STOPPER финансер не работает, нужно или сделать поддержку форматов или как-то мозг не ебать, или в разделители добавить кроме / еще всякие _,-. //$this->ensureNoOverlappingRoutes($routes); $routes = $this->sortRoutesByPriority($routes); // we don't want to cache unnecessary data foreach ($routes as $i => $route) { unset($routes[$i]['pattern'], $routes[$i]['priority']); } return $routes; }
/** * @dataProvider phpdocTagsProvider */ public function testGetPhpdocTags($expected, $phpdoc) { $this->assertEquals($expected, AnnotationReader::getPhpdocTags($phpdoc)); }