/** * Builds a new child and returns it. * * The child will have all the before functions from its parent. * It will also inherit of the prefix from its parent. * * @return RoutesCollection */ public function newChild($prefix = '') { $c = new RoutesCollection($prefix); $c->before(function ($scope) { foreach ($this->globalBefores as $b) { $scope->call($b); if ($scope->isRightResource === false) { return; } if ($scope->stopRoute === true) { return; } } }); $this->children[] = $c; $c->parent = $this; return $c; }
/** * Parses a class and registers all resources defined in it. * * This function will analyse the comments of each method of the class and create the appropriate routes. * The routes are created in a child RoutesCollection that is returned by this function. * * Recognized tokens are: * - @before (both) See description below * - @disabled (route only) This route is not created * - @method (route only) Pattern of the method to match * - @name (route only) Name of the route * - @pattern (route only) Sets the regex pattern of the part of a URL * - @prefix (class only) Sets the prefix of the RoutesCollection ; can be overwritten by calling RoutesCollection->prefix() * - @static (class only) Adds a path of static resources ; path is relative to the class location * - @url (route only) Pattern of the URL to match, see register() * - @uri (route only) Alias of @url * * The first before function of the new route collection will create an instance of the class and put it in $scope->this. * * The @before token has several possible syntaxes: * - @before {global_function} * where {global_function} is the name of a global function to be called before the handler * - @before {method} * where {method} is the name of a method of the current class to be called before the handler * - @before {class} * where {class} is a class name * the before function will simply invoke the given class and do nothing * - @before {class}::{method} {params} * where {class} is a class name, {method} is the name of a method of the class, and {params} can be eval'd as a PHP array * the before function will try to find in the scope an object whose type is the class, and call the method on it * - @before function({params}) { {php code} } * the function and code will be evaled as a PHP closure * - @before onlyif {anything} * where {anything} can be any of the other syntaxes above, and {code} is a status code * if the "anything part" returns false, then the route is considered not to match and another route will be tried (isRightResource is set to false) * - @before validate {code} {anything} * where {anything} can be any of the other syntaxes above, and {code} is a status code * if the "anything part" returns false, then the route is stopped and the status code is set to the response (stopRoute is set to true) * - @before ${varName} = {anything} * where {anything} can be any of the other syntaxes above, and {varName} is a status code * the variable $varName of the scope will be set to the return value of the "anything part" * * @param ReflectionClass $reflectionClass The class to parse * @param RoutesCollection $parent The parent of the collection that will be created * @return RoutesCollection */ public function parseClass(\ReflectionClass $reflectionClass, RoutesCollection $parent) { // building the new collection $newCollection = $parent->newChild(); $newCollection->before(function (Scope $scope) use($reflectionClass) { $scope->this = $scope->call($reflectionClass); }); // analyzing the doccomment of the class $classDocComment = self::parseDocComment($reflectionClass->getDocComment()); // handling @before if (isset($classDocComment['before'])) { foreach ($classDocComment['before'] as $before) { $newCollection->before(self::buildBeforeFunction($reflectionClass, $before)); } } // handling @prefix if (isset($classDocComment['prefix'])) { $newCollection->prefix(implode($classDocComment['prefix'])); } // handling @static if (isset($classDocComment['static'])) { foreach ($classDocComment['static'] as $path) { $newCollection->registerStaticDirectory(dirname($reflectionClass->getFileName()) . DIRECTORY_SEPARATOR . $path)->name($reflectionClass->getName()); } } // looping through each method of the class foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $methodReflection) { if (!($comment = $methodReflection->getDocComment())) { continue; } $parameters = self::parseDocComment($comment); // now analyzing parameters if (isset($parameters['url']) || isset($parameters['name'])) { if (isset($parameters['disabled'])) { continue; } // building the route if (isset($parameters['url'])) { $route = $newCollection->register($parameters['url']); } else { $route = $newCollection->register(); } // setting name of the route if (isset($parameters['name'])) { if (count($parameters['name']) > 1) { throw new \LogicException('A route cannot have multiple names'); } $route->name($parameters['name'][0]); } else { $route->name($reflectionClass->getName() . '::' . $methodReflection->getName()); } // setting the method if (isset($parameters['method'])) { $route->method($parameters['method'][0]); } // setting the pattern of the URL parts if (isset($parameters['pattern'])) { foreach ($parameters['pattern'] as $p => $value) { list($part, $val) = explode(' ', $value, 2); $route->pattern($part, $val); } } // setting the handler $route->handler(function (Scope $scope) use($methodReflection, $reflectionClass) { return $scope->call($methodReflection->getClosure($scope->this)); }); // setting the before functions if (isset($parameters['before'])) { foreach ($parameters['before'] as $before) { $route->before(self::buildBeforeFunction($reflectionClass, $before)); } } } } return $newCollection; }