2010-08-09 14:21:59 +01:00
< ? php
/*
2011-01-15 13:29:43 +00:00
* This file is part of the Symfony package .
2010-08-09 14:21:59 +01:00
*
2011-03-06 11:40:06 +00:00
* ( c ) Fabien Potencier < fabien @ symfony . com >
2010-08-09 14:21:59 +01:00
*
2011-01-15 13:29:43 +00:00
* For the full copyright and license information , please view the LICENSE
* file that was distributed with this source code .
2010-08-09 14:21:59 +01:00
*/
2011-01-15 13:29:43 +00:00
namespace Symfony\Component\HttpKernel\Controller ;
2013-01-09 09:41:17 +00:00
use Psr\Log\LoggerInterface ;
2011-01-15 13:29:43 +00:00
use Symfony\Component\HttpFoundation\Request ;
2010-08-09 14:21:59 +01:00
/**
* ControllerResolver .
*
* This implementation uses the '_controller' request attribute to determine
* the controller to execute and uses the request attributes to determine
* the controller method arguments .
*
2011-03-06 11:40:06 +00:00
* @ author Fabien Potencier < fabien @ symfony . com >
2010-08-09 14:21:59 +01:00
*/
2016-04-09 21:45:30 +01:00
class ControllerResolver implements ArgumentResolverInterface , ControllerResolverInterface
2010-08-09 14:21:59 +01:00
{
2011-03-23 18:47:16 +00:00
private $logger ;
2010-08-09 14:21:59 +01:00
2016-09-01 08:08:10 +01:00
/**
* If the ... $arg functionality is available .
*
* Requires at least PHP 5.6 . 0 or HHVM 3.9 . 1
*
* @ var bool
*/
private $supportsVariadic ;
2016-12-05 07:37:16 +00:00
/**
* If scalar types exists .
*
* @ var bool
*/
private $supportsScalarTypes ;
2010-08-09 14:21:59 +01:00
/**
* Constructor .
*
* @ param LoggerInterface $logger A LoggerInterface instance
*/
public function __construct ( LoggerInterface $logger = null )
{
$this -> logger = $logger ;
2016-09-01 08:08:10 +01:00
$this -> supportsVariadic = method_exists ( 'ReflectionParameter' , 'isVariadic' );
2016-12-05 07:37:16 +00:00
$this -> supportsScalarTypes = method_exists ( 'ReflectionParameter' , 'getType' );
2010-08-09 14:21:59 +01:00
}
/**
2013-11-20 02:35:05 +00:00
* { @ inheritdoc }
2010-08-09 14:21:59 +01:00
*
* This method looks for a '_controller' request attribute that represents
2011-01-23 11:10:45 +00:00
* the controller name ( a string like ClassName :: MethodName ) .
2010-08-09 14:21:59 +01:00
*/
public function getController ( Request $request )
{
if ( ! $controller = $request -> attributes -> get ( '_controller' )) {
if ( null !== $this -> logger ) {
2015-01-15 09:04:56 +00:00
$this -> logger -> warning ( 'Unable to look for the controller as the "_controller" parameter is missing.' );
2010-08-09 14:21:59 +01:00
}
return false ;
}
2014-03-26 18:20:25 +00:00
if ( is_array ( $controller )) {
2010-09-16 09:46:38 +01:00
return $controller ;
}
2014-03-26 18:20:25 +00:00
if ( is_object ( $controller )) {
if ( method_exists ( $controller , '__invoke' )) {
return $controller ;
}
throw new \InvalidArgumentException ( sprintf ( 'Controller "%s" for URI "%s" is not callable.' , get_class ( $controller ), $request -> getPathInfo ()));
}
2012-02-11 23:34:53 +00:00
if ( false === strpos ( $controller , ':' )) {
if ( method_exists ( $controller , '__invoke' )) {
2014-04-29 16:01:48 +01:00
return $this -> instantiateController ( $controller );
2012-02-11 23:34:53 +00:00
} elseif ( function_exists ( $controller )) {
return $controller ;
}
2011-08-08 22:16:52 +01:00
}
2013-04-20 21:07:10 +01:00
$callable = $this -> createController ( $controller );
2010-08-09 14:21:59 +01:00
2013-04-20 21:07:10 +01:00
if ( ! is_callable ( $callable )) {
2014-04-26 01:10:40 +01:00
throw new \InvalidArgumentException ( sprintf ( 'The controller for URI "%s" is not callable. %s' , $request -> getPathInfo (), $this -> getControllerError ( $callable )));
2010-08-09 14:21:59 +01:00
}
2013-04-20 21:07:10 +01:00
return $callable ;
2010-08-09 14:21:59 +01:00
}
/**
2013-11-20 02:35:05 +00:00
* { @ inheritdoc }
2016-03-16 07:56:41 +00:00
*
2016-04-09 21:45:30 +01:00
* @ deprecated This method is deprecated as of 3.1 and will be removed in 4.0 . Implement the ArgumentResolverInterface and inject it in the HttpKernel instead .
2010-08-09 14:21:59 +01:00
*/
public function getArguments ( Request $request , $controller )
{
2016-04-09 21:45:30 +01:00
@ trigger_error ( sprintf ( '%s is deprecated as of 3.1 and will be removed in 4.0. Implement the %s and inject it in the HttpKernel instead.' , __METHOD__ , ArgumentResolverInterface :: class ), E_USER_DEPRECATED );
2010-08-09 14:21:59 +01:00
2016-04-09 21:45:30 +01:00
if ( is_array ( $controller )) {
$r = new \ReflectionMethod ( $controller [ 0 ], $controller [ 1 ]);
} elseif ( is_object ( $controller ) && ! $controller instanceof \Closure ) {
$r = new \ReflectionObject ( $controller );
$r = $r -> getMethod ( '__invoke' );
} else {
$r = new \ReflectionFunction ( $controller );
}
return $this -> doGetArguments ( $request , $controller , $r -> getParameters ());
2011-08-08 22:20:53 +01:00
}
2016-03-16 07:56:41 +00:00
/**
2016-09-01 08:08:10 +01:00
* @ param Request $request
* @ param callable $controller
* @ param \ReflectionParameter [] $parameters
*
* @ return array The arguments to use when calling the action
2016-09-14 21:33:02 +01:00
*
2016-04-09 21:45:30 +01:00
* @ deprecated This method is deprecated as of 3.1 and will be removed in 4.0 . Implement the ArgumentResolverInterface and inject it in the HttpKernel instead .
2016-03-16 07:56:41 +00:00
*/
2011-08-08 22:20:53 +01:00
protected function doGetArguments ( Request $request , $controller , array $parameters )
{
2016-04-09 21:45:30 +01:00
@ trigger_error ( sprintf ( '%s is deprecated as of 3.1 and will be removed in 4.0. Implement the %s and inject it in the HttpKernel instead.' , __METHOD__ , ArgumentResolverInterface :: class ), E_USER_DEPRECATED );
$attributes = $request -> attributes -> all ();
$arguments = array ();
foreach ( $parameters as $param ) {
if ( array_key_exists ( $param -> name , $attributes )) {
2016-09-01 08:08:10 +01:00
if ( $this -> supportsVariadic && $param -> isVariadic () && is_array ( $attributes [ $param -> name ])) {
2016-04-09 21:45:30 +01:00
$arguments = array_merge ( $arguments , array_values ( $attributes [ $param -> name ]));
} else {
$arguments [] = $attributes [ $param -> name ];
}
} elseif ( $param -> getClass () && $param -> getClass () -> isInstance ( $request )) {
$arguments [] = $request ;
} elseif ( $param -> isDefaultValueAvailable ()) {
$arguments [] = $param -> getDefaultValue ();
2016-12-05 07:37:16 +00:00
} elseif ( $this -> supportsScalarTypes && $param -> hasType () && $param -> allowsNull ()) {
2016-09-01 08:08:10 +01:00
$arguments [] = null ;
2016-04-09 21:45:30 +01:00
} else {
if ( is_array ( $controller )) {
$repr = sprintf ( '%s::%s()' , get_class ( $controller [ 0 ]), $controller [ 1 ]);
} elseif ( is_object ( $controller )) {
$repr = get_class ( $controller );
} else {
$repr = $controller ;
}
throw new \RuntimeException ( sprintf ( 'Controller "%s" requires that you provide a value for the "$%s" argument (because there is no default value or because there is a non optional argument after this one).' , $repr , $param -> name ));
}
}
2010-08-09 14:21:59 +01:00
2016-04-09 21:45:30 +01:00
return $arguments ;
2010-08-09 14:21:59 +01:00
}
/**
* Returns a callable for the given controller .
*
* @ param string $controller A Controller string
*
2015-10-05 17:52:37 +01:00
* @ return callable A PHP callable
2012-12-16 12:02:54 +00:00
*
* @ throws \InvalidArgumentException
2010-08-09 14:21:59 +01:00
*/
protected function createController ( $controller )
{
if ( false === strpos ( $controller , '::' )) {
throw new \InvalidArgumentException ( sprintf ( 'Unable to find controller "%s".' , $controller ));
}
2011-12-11 20:16:22 +00:00
list ( $class , $method ) = explode ( '::' , $controller , 2 );
2010-08-09 14:21:59 +01:00
if ( ! class_exists ( $class )) {
throw new \InvalidArgumentException ( sprintf ( 'Class "%s" does not exist.' , $class ));
}
2014-04-29 16:01:48 +01:00
return array ( $this -> instantiateController ( $class ), $method );
}
/**
2015-08-24 07:53:33 +01:00
* Returns an instantiated controller .
2014-04-29 16:01:48 +01:00
*
* @ param string $class A class name
*
* @ return object
*/
protected function instantiateController ( $class )
{
return new $class ();
2010-08-09 14:21:59 +01:00
}
2014-04-26 01:10:40 +01:00
private function getControllerError ( $callable )
{
if ( is_string ( $callable )) {
if ( false !== strpos ( $callable , '::' )) {
$callable = explode ( '::' , $callable );
}
if ( class_exists ( $callable ) && ! method_exists ( $callable , '__invoke' )) {
return sprintf ( 'Class "%s" does not have a method "__invoke".' , $callable );
}
if ( ! function_exists ( $callable )) {
return sprintf ( 'Function "%s" does not exist.' , $callable );
}
}
if ( ! is_array ( $callable )) {
return sprintf ( 'Invalid type for controller given, expected string or array, got "%s".' , gettype ( $callable ));
}
if ( 2 !== count ( $callable )) {
return sprintf ( 'Invalid format for controller, expected array(controller, method) or controller::method.' );
}
list ( $controller , $method ) = $callable ;
if ( is_string ( $controller ) && ! class_exists ( $controller )) {
return sprintf ( 'Class "%s" does not exist.' , $controller );
}
$className = is_object ( $controller ) ? get_class ( $controller ) : $controller ;
if ( method_exists ( $controller , $method )) {
return sprintf ( 'Method "%s" on class "%s" should be public and non-abstract.' , $method , $className );
}
$collection = get_class_methods ( $controller );
$alternatives = array ();
foreach ( $collection as $item ) {
$lev = levenshtein ( $method , $item );
if ( $lev <= strlen ( $method ) / 3 || false !== strpos ( $item , $method )) {
$alternatives [] = $item ;
}
}
asort ( $alternatives );
$message = sprintf ( 'Expected method "%s" on class "%s"' , $method , $className );
if ( count ( $alternatives ) > 0 ) {
$message .= sprintf ( ', did you mean "%s"?' , implode ( '", "' , $alternatives ));
} else {
$message .= sprintf ( '. Available methods: "%s".' , implode ( '", "' , $collection ));
}
return $message ;
}
2010-08-09 14:21:59 +01:00
}