/**
  * Reads controller routes.
  *
  * @param \ReflectionClass $reflectionClass
  *
  * @return RestRouteCollection
  *
  * @throws \InvalidArgumentException
  */
 public function read(\ReflectionClass $reflectionClass)
 {
     $collection = new RestRouteCollection();
     $collection->addResource(new FileResource($reflectionClass->getFileName()));
     // read prefix annotation
     if ($annotation = $this->readClassAnnotation($reflectionClass, 'Prefix')) {
         $this->actionReader->setRoutePrefix($annotation->value);
     }
     // read name-prefix annotation
     if ($annotation = $this->readClassAnnotation($reflectionClass, 'NamePrefix')) {
         $this->actionReader->setNamePrefix($annotation->value);
     }
     $resource = array();
     // read route-resource annotation
     if ($annotation = $this->readClassAnnotation($reflectionClass, 'RouteResource')) {
         $resource = explode('_', $annotation->resource);
     } elseif ($reflectionClass->implementsInterface('FOS\\RestBundle\\Routing\\ClassResourceInterface')) {
         $resource = preg_split('/([A-Z][^A-Z]*)Controller/', $reflectionClass->getShortName(), -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
         if (empty($resource)) {
             throw new \InvalidArgumentException("Controller '{$reflectionClass->name}' does not identify a resource");
         }
     }
     // trim '/' at the start of the prefix
     if ('/' === substr($prefix = $this->actionReader->getRoutePrefix(), 0, 1)) {
         $this->actionReader->setRoutePrefix(substr($prefix, 1));
     }
     // read action routes into collection
     foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
         $this->actionReader->read($collection, $method, $resource);
     }
     $this->actionReader->setRoutePrefix(null);
     $this->actionReader->setNamePrefix(null);
     $this->actionReader->setParents(array());
     return $collection;
 }
示例#2
0
 /**
  * Reads controller routes.
  *
  * @param ReflectionClass $reflection
  *
  * @return RestRouteCollection
  */
 public function read(\ReflectionClass $reflection)
 {
     $collection = new RestRouteCollection();
     $collection->addResource(new FileResource($reflection->getFileName()));
     // read prefix annotation
     if ($annotation = $this->readClassAnnotation($reflection, 'Prefix')) {
         $this->actionReader->setRoutePrefix($annotation->value);
     }
     // read name-prefix annotation
     if ($annotation = $this->readClassAnnotation($reflection, 'NamePrefix')) {
         $this->actionReader->setNamePrefix($annotation->value);
     }
     // trim '/' at the start of the prefix
     if ('/' === substr($prefix = $this->actionReader->getRoutePrefix(), 0, 1)) {
         $this->actionReader->setRoutePrefix(substr($prefix, 1));
     }
     // read action routes into collection
     foreach ($reflection->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
         $this->actionReader->read($collection, $method);
     }
     $this->actionReader->setRoutePrefix(null);
     $this->actionReader->setNamePrefix(null);
     $this->actionReader->setParents(array());
     return $collection;
 }
 /**
  * @param RestRouteCollection $collection
  * @param string              $routeName
  * @param Route               $route
  * @param bool                $isCollection
  * @param bool                $isInflectable
  * @param RouteAnnotation     $annotation
  */
 private function addRoute(RestRouteCollection $collection, $routeName, $route, $isCollection, $isInflectable, RouteAnnotation $annotation = null)
 {
     if ($annotation && null !== $annotation->getName()) {
         $options = $annotation->getOptions();
         if (isset($options['method_prefix']) && false === $options['method_prefix']) {
             $routeName = $annotation->getName();
         } else {
             $routeName = $routeName . $annotation->getName();
         }
     }
     $fullRouteName = $this->namePrefix . $routeName;
     if ($isCollection && !$isInflectable) {
         $collection->add($this->namePrefix . self::COLLECTION_ROUTE_PREFIX . $routeName, $route);
         if (!$collection->get($fullRouteName)) {
             $collection->add($fullRouteName, clone $route);
         }
     } else {
         $collection->add($fullRouteName, $route);
     }
 }
 /**
  * Loads a Routes collection by parsing Controller method names.
  *
  * @param   string  $controller Some identifier for the controller
  * @param   string  $type       The resource type
  *
  * @return  RouteCollection     A RouteCollection instance
  */
 public function load($controller, $type = null)
 {
     if (class_exists($controller)) {
         // full class name
         $class = $controller;
         $controllerPrefix = $class . '::';
     } elseif (false !== strpos($controller, ':')) {
         // bundle:controller notation
         try {
             $notation = $this->parser->parse($controller . ':method');
             list($class, $method) = explode('::', $notation);
             $controllerPrefix = $class . '::';
         } catch (\Exception $e) {
             throw new \InvalidArgumentException(sprintf('Can\'t locate "%s" controller.', $controller));
         }
     } elseif ($this->container->has($controller)) {
         // service_id
         $controllerPrefix = $controller . ':';
         // FIXME: this is ugly, but I do not see any good alternative
         $this->container->enterScope('request');
         $this->container->set('request', new Request());
         $class = get_class($this->container->get($controller));
         $this->container->leaveScope('request');
     }
     if (empty($class)) {
         throw new \InvalidArgumentException(sprintf('Class could not be determined for Controller identified by "%s".', $controller));
     }
     // Check that every passed parent has non-empty singular name
     foreach ($this->parents as $parent) {
         if (empty($parent) || '/' === substr($parent, -1)) {
             throw new \InvalidArgumentException('All parent controllers must have ::getSINGULAR_NAME() action');
         }
     }
     $class = new \ReflectionClass($class);
     $collection = new RestRouteCollection();
     $collection->addResource(new FileResource($class->getFileName()));
     $prefixAnnotationClass = 'FOS\\RestBundle\\Controller\\Annotations\\Prefix';
     $prefix = $this->reader->getClassAnnotation($class, $prefixAnnotationClass);
     if ($prefix) {
         $this->prefix = $prefix->value;
     }
     $namePrefixAnnotationClass = 'FOS\\RestBundle\\Controller\\Annotations\\NamePrefix';
     $namePrefix = $this->reader->getClassAnnotation($class, $namePrefixAnnotationClass);
     if ($namePrefix) {
         $this->namePrefix = $namePrefix->value;
     }
     // Trim "/" at the start
     if (null !== $this->prefix && isset($this->prefix[0]) && '/' === $this->prefix[0]) {
         $this->prefix = substr($this->prefix, 1);
     }
     $routeAnnotationClass = 'FOS\\RestBundle\\Controller\\Annotations\\Route';
     $patternStartRoute = $this->reader->getClassAnnotation($class, $routeAnnotationClass);
     $patternStart = null;
     if ($patternStartRoute) {
         $patternStart = trim($patternStartRoute->getPattern(), "/");
     }
     $routes = array();
     foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
         $matches = array();
         // If method name starts with underscore - skip
         if ('_' === substr($method->getName(), 0, 1)) {
             continue;
         }
         // If method has @NoRoute annotation - skip
         $noAnnotationClass = 'FOS\\RestBundle\\Controller\\Annotations\\NoRoute';
         if (null !== $this->reader->getMethodAnnotation($method, $noAnnotationClass)) {
             continue;
         }
         if (preg_match('/([a-z][_a-z0-9]+)(.*)Action/', $method->getName(), $matches)) {
             $httpMethod = $matches[1];
             $resources = preg_split('/([A-Z][^A-Z]*)/', $matches[2], -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
             $arguments = $method->getParameters();
             // Ignore arguments that are or extend from Symfony\Component\HttpFoundation\Request
             foreach ($arguments as $key => $argument) {
                 $class = $argument->getClass();
                 if ($class && ($class->getName() === 'Symfony\\Component\\HttpFoundation\\Request' || is_subclass_of($class->getName(), 'Symfony\\Component\\HttpFoundation\\Request'))) {
                     unset($arguments[$key]);
                 }
             }
             // If we have 1 resource passed & 1 argument, then it's object call, so
             // we can set collection singular name
             if (1 === count($resources) && 1 === count($arguments) - count($this->parents)) {
                 $collection->setSingularName($resources[0]);
             }
             // If we have parents passed - merge them with own resource names
             if (count($this->parents)) {
                 $resources = array_merge($this->parents, $resources);
             }
             $urlParts = array();
             $routeName = $httpMethod;
             if (empty($resources)) {
                 $resources[] = null;
             }
             foreach ($resources as $i => $resource) {
                 if (null !== $resource) {
                     $routeName .= '_' . basename($resource);
                 }
                 // If we already added all parent routes paths to URL & we have prefix - add it
                 if (!empty($this->prefix) && $i === count($this->parents)) {
                     $urlParts[] = $this->prefix;
                 }
                 // If we have argument for current resource, then it's object. Otherwise - it's collection
                 if (isset($arguments[$i])) {
                     if ($patternStart) {
                         $urlParts[] = strtolower($patternStart) . '/{' . $arguments[$i]->getName() . '}';
                     } elseif (null !== $resource) {
                         $urlParts[] = strtolower(Pluralization::pluralize($resource)) . '/{' . $arguments[$i]->getName() . '}';
                     } else {
                         $urlParts[] = '{' . $arguments[$i]->getName() . '}';
                     }
                 } else {
                     if ($patternStart) {
                         $urlParts[] = $patternStart;
                     } elseif (null !== $resource) {
                         $urlParts[] = strtolower($resource);
                     }
                 }
             }
             // If passed method is not valid HTTP method
             // then it's either a hypertext driver,
             // a custom object (PUT) or collection (GET) method
             if (!in_array($httpMethod, $this->availableHTTPMethods)) {
                 $urlParts[] = $httpMethod;
                 // allow hypertext as the engine of application state
                 // through conventional GET actions
                 if (in_array($httpMethod, $this->availableConventionalActions)) {
                     $httpMethod = 'get';
                 } else {
                     //custom object
                     $httpMethod = 'post';
                     // resource collection
                     if (count($arguments) < count($resources)) {
                         $httpMethod = 'get';
                     }
                 }
             }
             $pattern = implode('/', $urlParts);
             $defaults = array('_controller' => $controllerPrefix . $method->getName(), '_format' => $this->defaultFormat);
             $requirements = array('_method' => strtoupper($httpMethod));
             $options = array();
             // Read annotations
             foreach ($this->annotationClasses as $annotationClass) {
                 $routeAnnnotation = $this->reader->getMethodAnnotation($method, $annotationClass);
                 if (null !== $routeAnnnotation) {
                     $annoRequirements = $routeAnnnotation->getRequirements();
                     if (!isset($annoRequirements['_method']) || null === $annoRequirements['_method']) {
                         $annoRequirements['_method'] = $requirements['_method'];
                     }
                     $pattern = $routeAnnnotation->getPattern() ?: $pattern;
                     $requirements = array_merge($requirements, $annoRequirements);
                     $options = array_merge($options, $routeAnnnotation->getOptions());
                     $defaults = array_merge($defaults, $routeAnnnotation->getDefaults());
                     break;
                 }
             }
             //Adding in the optional _format param for serialization
             $pattern .= ".{_format}";
             // Create route with gathered parameters
             $route = new Route($pattern, $defaults, $requirements, $options);
             $routeName = $this->namePrefix . strtolower($routeName);
             // Move custom actions at the beginning, default at the end
             if (!preg_match('/^(' . implode('|', $this->availableHTTPMethods) . ')/', $routeName)) {
                 array_unshift($routes, array('name' => $routeName, 'route' => $route));
             } else {
                 $routes[] = array('name' => $routeName, 'route' => $route);
             }
         }
     }
     foreach ($routes as $routeInfo) {
         $collection->add($routeInfo['name'], $routeInfo['route']);
     }
     $this->prefix = null;
     $this->namePrefix = null;
     return $collection;
 }
示例#5
0
 /**
  * Reads action route.
  *
  * @param RestRouteCollection $collection route collection to read into
  * @param \ReflectionMethod   $method     method reflection
  *
  * @return Route
  */
 public function read(RestRouteCollection $collection, \ReflectionMethod $method)
 {
     // check that every route parent has non-empty singular name
     foreach ($this->parents as $parent) {
         if (empty($parent) || '/' === substr($parent, -1)) {
             throw new \InvalidArgumentException("Every parent controller must have `get{SINGULAR}Action(\$id)` method\n" . "where {SINGULAR} is a singular form of associated object");
         }
     }
     // if method is not readable - skip
     if (!$this->isMethodReadable($method)) {
         return;
     }
     // if we can't get http-method and resources from method name - skip
     $httpMethodAndResources = $this->getHttpMethodAndResourcesFromMethod($method);
     if (!$httpMethodAndResources) {
         return;
     }
     list($httpMethod, $resources) = $httpMethodAndResources;
     $arguments = $this->getMethodArguments($method);
     // if we have only 1 resource & 1 argument passed, then it's object call, so
     // we can set collection singular name
     if (1 === count($resources) && 1 === count($arguments) - count($this->parents)) {
         $collection->setSingularName($resources[0]);
     }
     // if we have parents passed - merge them with own resource names
     if (count($this->parents)) {
         $resources = array_merge($this->parents, $resources);
     }
     if (empty($resources)) {
         $resources[] = null;
     }
     $routeName = $httpMethod . $this->generateRouteName($resources);
     $urlParts = $this->generateUrlParts($resources, $arguments);
     // if passed method is not valid HTTP method then it's either
     // a hypertext driver, a custom object (PUT) or collection (GET)
     // method
     if (!in_array($httpMethod, $this->availableHTTPMethods)) {
         $urlParts[] = $httpMethod;
         $httpMethod = $this->getCustomHttpMethod($httpMethod, $resources, $arguments);
     }
     // generated parameters
     $routeName = $this->namePrefix . strtolower($routeName);
     $pattern = implode('/', $urlParts);
     $defaults = array('_controller' => $method->getName());
     $requirements = array('_method' => strtoupper($httpMethod));
     $options = array();
     $annotation = $this->readRouteAnnotation($method);
     if ($annotation) {
         $annoRequirements = $annotation->getRequirements();
         if (!isset($annoRequirements['_method'])) {
             $annoRequirements['_method'] = $requirements['_method'];
         }
         $pattern = $annotation->getPattern() ?: $pattern;
         $requirements = array_merge($requirements, $annoRequirements);
         $options = array_merge($options, $annotation->getOptions());
         $defaults = array_merge($defaults, $annotation->getDefaults());
     }
     // add route to collection
     $collection->add($routeName, new Route($pattern . '.{_format}', $defaults, $requirements, $options));
 }
示例#6
0
 /**
  * @param RestRouteCollection $collection
  * @param string              $routeName
  * @param Route               $route
  * @param bool                $isCollection
  * @param bool                $isInflectable
  * @param object              $annotation
  */
 private function addRoute(RestRouteCollection $collection, $routeName, $route, $isCollection, $isInflectable, $annotation = null)
 {
     if ($annotation) {
         $routeName = $routeName . $annotation->getName();
     }
     if ($isCollection && !$isInflectable) {
         $collection->add(self::COLLECTION_ROUTE_PREFIX . $routeName, $route);
         if (!$collection->get($routeName)) {
             $collection->add($routeName, clone $route);
         }
     } else {
         $collection->add($routeName, $route);
     }
 }