diff --git a/composer.json b/composer.json index 666a261..8b91bc0 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,7 @@ "php": ">=5.5.9", "php-di/php-di": "~5.0", "php-di/invoker": "~1.3", - "silex/silex" : "~2.0@dev", + "silex/silex" : "~2.0", "pimple/pimple" : "~3.0" }, "require-dev": { diff --git a/src/Application.php b/src/Application.php index 54fc96f..f1b8808 100644 --- a/src/Application.php +++ b/src/Application.php @@ -3,28 +3,16 @@ namespace DI\Bridge\Silex; use DI\Bridge\Silex\Container\ContainerInteropProxy; -use DI\Bridge\Silex\Controller\ControllerResolver; -use DI\Bridge\Silex\MiddlewareListener; -use DI\Bridge\Silex\ConverterListener; -use Silex\EventListener\LocaleListener; -use Silex\EventListener\StringToResponseListener; -use Silex\LazyUrlMatcher; -use Symfony\Component\HttpKernel\Kernel; -use Symfony\Component\HttpKernel\HttpKernelInterface; -use Symfony\Component\HttpKernel\KernelEvents; -use Symfony\Component\HttpKernel\Event\GetResponseEvent; -use Symfony\Component\HttpKernel\Event\FilterResponseEvent; -use Symfony\Component\HttpKernel\Event\PostResponseEvent; -use Symfony\Component\HttpKernel\EventListener\ResponseListener; -use Symfony\Component\HttpKernel\EventListener\RouterListener; +use DI\Bridge\Silex\Provider\HttpKernelServiceProvider; use DI\Container; use DI\ContainerBuilder; use Interop\Container\ContainerInterface; use Invoker\CallableResolver; -use Invoker\Reflection\CallableReflection; -use Invoker\ParameterResolver\AssociativeArrayResolver; -use Invoker\ParameterResolver\Container\TypeHintContainerResolver; -use Invoker\ParameterResolver\ResolverChain; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Event\PostResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; /** * Replacement for the Silex Application class to use PHP-DI instead of Pimple. @@ -72,51 +60,8 @@ public function __construct(ContainerBuilder $containerBuilder = null, array $va return new CallableResolver($this->containerInteropProxy); }; - // Override the controller resolver with ours - $this['resolver'] = function () { - return new ControllerResolver( - $this['phpdi.callable_resolver'], - new ResolverChain([ - new AssociativeArrayResolver, - new TypeHintContainerResolver($this->containerInteropProxy), - ]) - ); - }; - - // Override the callback resolver with ours - $this['callback_resolver'] = function () { - return new CallbackResolver( - $this, - $this['phpdi.callable_resolver'] - ); - }; - - // Override the dispatcher with ours to use our event listeners - $this['dispatcher'] = $this->share(function () { - /** - * @var EventDispatcherInterface - */ - $dispatcher = new $this['dispatcher_class'](); - - $urlMatcher = new LazyUrlMatcher(function () { - return $this['url_matcher']; - }); - if (Kernel::VERSION_ID >= 20800) { - $dispatcher->addSubscriber(new RouterListener($urlMatcher, $this['request_stack'], $this['request_context'], $this['logger'])); - } else { - $dispatcher->addSubscriber(new RouterListener($urlMatcher, $this['request_context'], $this['logger'], $this['request_stack'])); - } - $dispatcher->addSubscriber(new LocaleListener($this, $urlMatcher, $this['request_stack'])); - if (isset($this['exception_handler'])) { - $dispatcher->addSubscriber($this['exception_handler']); - } - $dispatcher->addSubscriber(new ResponseListener($this['charset'])); - $dispatcher->addSubscriber(new MiddlewareListener($this, $this->callbackInvoker)); - $dispatcher->addSubscriber(new ConverterListener($this['routes'], $this['callback_resolver'], $this->callbackInvoker)); - $dispatcher->addSubscriber(new StringToResponseListener()); - - return $dispatcher; - }); + // Register own HttpKernelServiceProvider which overrides some defaults. + $this->register(new HttpKernelServiceProvider($this->containerInteropProxy, $this->callbackInvoker)); } public function offsetGet($id) @@ -151,7 +96,7 @@ public function getPhpDi() public function before($callback, $priority = 0) { $this->on(KernelEvents::REQUEST, function (GetResponseEvent $event) use ($callback) { - if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) { + if (!$event->isMasterRequest()) { return; } @@ -175,7 +120,7 @@ public function before($callback, $priority = 0) public function after($callback, $priority = 0) { $this->on(KernelEvents::RESPONSE, function (FilterResponseEvent $event) use ($callback) { - if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) { + if (!$event->isMasterRequest()) { return; } @@ -208,7 +153,7 @@ public function finish($callback, $priority = 0) $request = $event->getRequest(); $response = $event->getResponse(); $middleware = $this['callback_resolver']->resolveCallback($callback); - $ret = $this->callbackInvoker->call($middleware, [ + $this->callbackInvoker->call($middleware, [ // type hints 'Symfony\Component\HttpFoundation\Request' => $request, 'Symfony\Component\HttpFoundation\Response' => $response, @@ -218,12 +163,6 @@ public function finish($callback, $priority = 0) 2 => $this, ]); - if ($ret instanceof Response) { - $event->setResponse($ret); - } elseif (null !== $ret) { - throw new \RuntimeException('An after middleware returned an invalid response value. Must return null or an instance of Response.'); - } - }, $priority); } } diff --git a/src/Provider/HttpKernelServiceProvider.php b/src/Provider/HttpKernelServiceProvider.php new file mode 100644 index 0000000..1ca67da --- /dev/null +++ b/src/Provider/HttpKernelServiceProvider.php @@ -0,0 +1,98 @@ + + */ +class HttpKernelServiceProvider implements ServiceProviderInterface, EventListenerProviderInterface +{ + /** + * @var ContainerInterface + */ + private $container; + + /** + * @var CallbackInvoker + */ + private $callbackInvoker; + + /** + * @param ContainerInterface $container + * @param CallbackInvoker $callbackInvoker + */ + public function __construct(ContainerInterface $container, CallbackInvoker $callbackInvoker) + { + $this->container = $container; + $this->callbackInvoker = $callbackInvoker; + } + + public function register(\Pimple\Container $app) + { + // Override the controller resolver with ours. + $app['resolver'] = function ($app) { + return new ControllerResolver( + $app['phpdi.callable_resolver'], + new ResolverChain([ + new AssociativeArrayResolver, + new TypeHintContainerResolver($this->container), + ]) + ); + }; + + // Override the callback resolver with ours. + $app['callback_resolver'] = function ($app) { + return new CallbackResolver( + $app, + $app['phpdi.callable_resolver'] + ); + }; + } + + public function subscribe(\Pimple\Container $app, EventDispatcherInterface $dispatcher) + { + // Remove the Silex listeners first + $this->removeSubscriber($dispatcher, \Silex\EventListener\MiddlewareListener::class); + $this->removeSubscriber($dispatcher, \Silex\EventListener\ConverterListener::class); + + // And register ours instead + $dispatcher->addSubscriber(new MiddlewareListener($app, $this->callbackInvoker)); + $dispatcher->addSubscriber(new ConverterListener($app['routes'], $app['callback_resolver'], $this->callbackInvoker)); + } + + /** + * Removes an event subscriber by its class name. + * + * @param EventDispatcherInterface $dispatcher + * @param string $subscriberClass + */ + private function removeSubscriber(EventDispatcherInterface $dispatcher, $subscriberClass) + { + if (!is_subclass_of($subscriberClass, EventSubscriberInterface::class, true)) { + throw new \InvalidArgumentException('$subscriberClass must implement ' . EventSubscriberInterface::class); + } + + /** @var EventSubscriberInterface $subscriberClass */ + foreach ($subscriberClass::getSubscribedEvents() as $eventName => $params) { + foreach ($dispatcher->getListeners($eventName) as $listener) { + if (is_array($listener) && is_a($listener[0], $subscriberClass, true)) { + $dispatcher->removeListener($eventName, $listener); + } + } + } + } +} diff --git a/tests/ApplicationTest.php b/tests/ApplicationTest.php index 2435bf3..73199b1 100644 --- a/tests/ApplicationTest.php +++ b/tests/ApplicationTest.php @@ -61,6 +61,7 @@ public function the_callback_resolver_should_be_registered_as_a_service() { $app = new Application(); + $this->assertInstanceOf('Closure', $app->raw('callback_resolver')); $this->assertInstanceOf('DI\Bridge\Silex\CallbackResolver', $app['callback_resolver']); } }