diff --git a/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php index 1b6540a209..6294845c72 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php @@ -98,13 +98,25 @@ class RequestDataCollector extends DataCollector implements EventSubscriberInter if (isset($this->controllers[$request])) { $controller = $this->controllers[$request]; if (is_array($controller)) { - $r = new \ReflectionMethod($controller[0], $controller[1]); - $this->data['controller'] = array( - 'class' => get_class($controller[0]), - 'method' => $controller[1], - 'file' => $r->getFilename(), - 'line' => $r->getStartLine(), - ); + try { + $r = new \ReflectionMethod($controller[0], $controller[1]); + $this->data['controller'] = array( + 'class' => is_object($controller[0]) ? get_class($controller[0]) : $controller[0], + 'method' => $controller[1], + 'file' => $r->getFilename(), + 'line' => $r->getStartLine(), + ); + } catch (\ReflectionException $re) { + if (is_callable($controller)) { + // using __call or __callStatic + $this->data['controller'] = array( + 'class' => is_object($controller[0]) ? get_class($controller[0]) : $controller[0], + 'method' => $controller[1], + 'file' => 'n/a', + 'line' => 'n/a', + ); + } + } } elseif ($controller instanceof \Closure) { $this->data['controller'] = 'Closure'; } else { diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php index d5075f2bf7..10322b132d 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php @@ -11,10 +11,14 @@ namespace Symfony\Component\HttpKernel\Tests\DataCollector; +use Symfony\Component\HttpKernel\HttpKernel; +use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\DataCollector\RequestDataCollector; +use Symfony\Component\HttpKernel\Event\FilterControllerEvent; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Cookie; +use Symfony\Component\EventDispatcher\EventDispatcher; class RequestDataCollectorTest extends \PHPUnit_Framework_TestCase { @@ -50,6 +54,95 @@ class RequestDataCollectorTest extends \PHPUnit_Framework_TestCase $this->assertEquals('application/json',$c->getContentType()); } + /** + * Test various types of controller callables. + * + * @dataProvider provider + */ + public function testControllerInspection(Request $request, Response $response) + { + // make sure we always match the line number + $r1 = new \ReflectionMethod($this, 'testControllerInspection'); + $r2 = new \ReflectionMethod($this, 'staticControllerMethod'); + // test name, callable, expected + $controllerTests = array( + array( + '"Regular" callable', + array($this, 'testControllerInspection'), + array( + 'class' => 'Symfony\Component\HttpKernel\Tests\DataCollector\RequestDataCollectorTest', + 'method' => 'testControllerInspection', + 'file' => __FILE__, + 'line' => $r1->getStartLine() + ), + ), + + array( + 'Closure', + function() { return 'foo'; }, + 'Closure', + ), + + array( + 'Static callback as string', + 'Symfony\Component\HttpKernel\Tests\DataCollector\RequestDataCollectorTest::staticControllerMethod', + 'Symfony\Component\HttpKernel\Tests\DataCollector\RequestDataCollectorTest::staticControllerMethod', + ), + + array( + 'Static callable with instance', + array($this, 'staticControllerMethod'), + array( + 'class' => 'Symfony\Component\HttpKernel\Tests\DataCollector\RequestDataCollectorTest', + 'method' => 'staticControllerMethod', + 'file' => __FILE__, + 'line' => $r2->getStartLine() + ), + ), + + array( + 'Static callable with class name', + array('Symfony\Component\HttpKernel\Tests\DataCollector\RequestDataCollectorTest', 'staticControllerMethod'), + array( + 'class' => 'Symfony\Component\HttpKernel\Tests\DataCollector\RequestDataCollectorTest', + 'method' => 'staticControllerMethod', + 'file' => __FILE__, + 'line' => $r2->getStartLine() + ), + ), + + array( + 'Callable with instance depending on __call()', + array($this, 'magicMethod'), + array( + 'class' => 'Symfony\Component\HttpKernel\Tests\DataCollector\RequestDataCollectorTest', + 'method' => 'magicMethod', + 'file' => 'n/a', + 'line' => 'n/a' + ), + ), + + array( + 'Callable with class name depending on __callStatic()', + array('Symfony\Component\HttpKernel\Tests\DataCollector\RequestDataCollectorTest', 'magicMethod'), + array( + 'class' => 'Symfony\Component\HttpKernel\Tests\DataCollector\RequestDataCollectorTest', + 'method' => 'magicMethod', + 'file' => 'n/a', + 'line' => 'n/a' + ), + ), + ); + + $c = new RequestDataCollector(); + + foreach ($controllerTests as $controllerTest) { + $this->injectController($c, $controllerTest[1], $request); + $c->collect($request, $response); + $this->assertEquals($controllerTest[2], $c->getController(), sprintf('Testing: %s', $controllerTest[0])); + } + } + public function provider() { if (!class_exists('Symfony\Component\HttpFoundation\Request')) { @@ -71,4 +164,39 @@ class RequestDataCollectorTest extends \PHPUnit_Framework_TestCase ); } + /** + * Inject the given controller callable into the data collector. + */ + protected function injectController($collector, $controller, $request) + { + $resolver = $this->getMock('Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface'); + $httpKernel = new HttpKernel(new EventDispatcher(), $resolver); + $event = new FilterControllerEvent($httpKernel, $controller, $request, HttpKernelInterface::MASTER_REQUEST); + $collector->onKernelController($event); + } + + /** + * Dummy method used as controller callable + */ + public static function staticControllerMethod() + { + throw new \LogicException('Unexpected method call'); + } + + /** + * Magic method to allow non existing methods to be called and delegated. + */ + public function __call($method, $args) + { + throw new \LogicException('Unexpected method call'); + } + + /** + * Magic method to allow non existing methods to be called and delegated. + */ + public static function __callStatic($method, $args) + { + throw new \LogicException('Unexpected method call'); + } + }