vendor/silex/silex/src/Silex/Application.php line 477

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Silex framework.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Silex;
  11. use Pimple\Container;
  12. use Pimple\ServiceProviderInterface;
  13. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  14. use Symfony\Component\HttpFoundation\BinaryFileResponse;
  15. use Symfony\Component\HttpKernel\HttpKernelInterface;
  16. use Symfony\Component\HttpKernel\TerminableInterface;
  17. use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
  18. use Symfony\Component\HttpKernel\Event\GetResponseEvent;
  19. use Symfony\Component\HttpKernel\Event\PostResponseEvent;
  20. use Symfony\Component\HttpKernel\Exception\HttpException;
  21. use Symfony\Component\HttpKernel\KernelEvents;
  22. use Symfony\Component\HttpFoundation\Request;
  23. use Symfony\Component\HttpFoundation\Response;
  24. use Symfony\Component\HttpFoundation\RedirectResponse;
  25. use Symfony\Component\HttpFoundation\StreamedResponse;
  26. use Symfony\Component\HttpFoundation\JsonResponse;
  27. use Silex\Api\BootableProviderInterface;
  28. use Silex\Api\EventListenerProviderInterface;
  29. use Silex\Api\ControllerProviderInterface;
  30. use Silex\Provider\ExceptionHandlerServiceProvider;
  31. use Silex\Provider\RoutingServiceProvider;
  32. use Silex\Provider\HttpKernelServiceProvider;
  33. /**
  34.  * The Silex framework class.
  35.  *
  36.  * @author Fabien Potencier <fabien@symfony.com>
  37.  */
  38. class Application extends Container implements HttpKernelInterfaceTerminableInterface
  39. {
  40.     const VERSION '2.2.3';
  41.     const EARLY_EVENT 512;
  42.     const LATE_EVENT = -512;
  43.     protected $providers = [];
  44.     protected $booted false;
  45.     /**
  46.      * Instantiate a new Application.
  47.      *
  48.      * Objects and parameters can be passed as argument to the constructor.
  49.      *
  50.      * @param array $values the parameters or objects
  51.      */
  52.     public function __construct(array $values = [])
  53.     {
  54.         parent::__construct();
  55.         $this['request.http_port'] = 80;
  56.         $this['request.https_port'] = 443;
  57.         $this['debug'] = false;
  58.         $this['charset'] = 'UTF-8';
  59.         $this['logger'] = null;
  60.         $this->register(new HttpKernelServiceProvider());
  61.         $this->register(new RoutingServiceProvider());
  62.         $this->register(new ExceptionHandlerServiceProvider());
  63.         foreach ($values as $key => $value) {
  64.             $this[$key] = $value;
  65.         }
  66.     }
  67.     /**
  68.      * Registers a service provider.
  69.      *
  70.      * @param ServiceProviderInterface $provider A ServiceProviderInterface instance
  71.      * @param array                    $values   An array of values that customizes the provider
  72.      *
  73.      * @return Application
  74.      */
  75.     public function register(ServiceProviderInterface $provider, array $values = [])
  76.     {
  77.         $this->providers[] = $provider;
  78.         parent::register($provider$values);
  79.         return $this;
  80.     }
  81.     /**
  82.      * Boots all service providers.
  83.      *
  84.      * This method is automatically called by handle(), but you can use it
  85.      * to boot all service providers when not handling a request.
  86.      */
  87.     public function boot()
  88.     {
  89.         if ($this->booted) {
  90.             return;
  91.         }
  92.         $this->booted true;
  93.         foreach ($this->providers as $provider) {
  94.             if ($provider instanceof EventListenerProviderInterface) {
  95.                 $provider->subscribe($this$this['dispatcher']);
  96.             }
  97.             if ($provider instanceof BootableProviderInterface) {
  98.                 $provider->boot($this);
  99.             }
  100.         }
  101.     }
  102.     /**
  103.      * Maps a pattern to a callable.
  104.      *
  105.      * You can optionally specify HTTP methods that should be matched.
  106.      *
  107.      * @param string $pattern Matched route pattern
  108.      * @param mixed  $to      Callback that returns the response when matched
  109.      *
  110.      * @return Controller
  111.      */
  112.     public function match($pattern$to null)
  113.     {
  114.         return $this['controllers']->match($pattern$to);
  115.     }
  116.     /**
  117.      * Maps a GET request to a callable.
  118.      *
  119.      * @param string $pattern Matched route pattern
  120.      * @param mixed  $to      Callback that returns the response when matched
  121.      *
  122.      * @return Controller
  123.      */
  124.     public function get($pattern$to null)
  125.     {
  126.         return $this['controllers']->get($pattern$to);
  127.     }
  128.     /**
  129.      * Maps a POST request to a callable.
  130.      *
  131.      * @param string $pattern Matched route pattern
  132.      * @param mixed  $to      Callback that returns the response when matched
  133.      *
  134.      * @return Controller
  135.      */
  136.     public function post($pattern$to null)
  137.     {
  138.         return $this['controllers']->post($pattern$to);
  139.     }
  140.     /**
  141.      * Maps a PUT request to a callable.
  142.      *
  143.      * @param string $pattern Matched route pattern
  144.      * @param mixed  $to      Callback that returns the response when matched
  145.      *
  146.      * @return Controller
  147.      */
  148.     public function put($pattern$to null)
  149.     {
  150.         return $this['controllers']->put($pattern$to);
  151.     }
  152.     /**
  153.      * Maps a DELETE request to a callable.
  154.      *
  155.      * @param string $pattern Matched route pattern
  156.      * @param mixed  $to      Callback that returns the response when matched
  157.      *
  158.      * @return Controller
  159.      */
  160.     public function delete($pattern$to null)
  161.     {
  162.         return $this['controllers']->delete($pattern$to);
  163.     }
  164.     /**
  165.      * Maps an OPTIONS request to a callable.
  166.      *
  167.      * @param string $pattern Matched route pattern
  168.      * @param mixed  $to      Callback that returns the response when matched
  169.      *
  170.      * @return Controller
  171.      */
  172.     public function options($pattern$to null)
  173.     {
  174.         return $this['controllers']->options($pattern$to);
  175.     }
  176.     /**
  177.      * Maps a PATCH request to a callable.
  178.      *
  179.      * @param string $pattern Matched route pattern
  180.      * @param mixed  $to      Callback that returns the response when matched
  181.      *
  182.      * @return Controller
  183.      */
  184.     public function patch($pattern$to null)
  185.     {
  186.         return $this['controllers']->patch($pattern$to);
  187.     }
  188.     /**
  189.      * Adds an event listener that listens on the specified events.
  190.      *
  191.      * @param string   $eventName The event to listen on
  192.      * @param callable $callback  The listener
  193.      * @param int      $priority  The higher this value, the earlier an event
  194.      *                            listener will be triggered in the chain (defaults to 0)
  195.      */
  196.     public function on($eventName$callback$priority 0)
  197.     {
  198.         if ($this->booted) {
  199.             $this['dispatcher']->addListener($eventName$this['callback_resolver']->resolveCallback($callback), $priority);
  200.             return;
  201.         }
  202.         $this->extend('dispatcher', function (EventDispatcherInterface $dispatcher$app) use ($callback$priority$eventName) {
  203.             $dispatcher->addListener($eventName$app['callback_resolver']->resolveCallback($callback), $priority);
  204.             return $dispatcher;
  205.         });
  206.     }
  207.     /**
  208.      * Registers a before filter.
  209.      *
  210.      * Before filters are run before any route has been matched.
  211.      *
  212.      * @param mixed $callback Before filter callback
  213.      * @param int   $priority The higher this value, the earlier an event
  214.      *                        listener will be triggered in the chain (defaults to 0)
  215.      */
  216.     public function before($callback$priority 0)
  217.     {
  218.         $app $this;
  219.         $this->on(KernelEvents::REQUEST, function (GetResponseEvent $event) use ($callback$app) {
  220.             if (!$event->isMasterRequest()) {
  221.                 return;
  222.             }
  223.             $ret call_user_func($app['callback_resolver']->resolveCallback($callback), $event->getRequest(), $app);
  224.             if ($ret instanceof Response) {
  225.                 $event->setResponse($ret);
  226.             }
  227.         }, $priority);
  228.     }
  229.     /**
  230.      * Registers an after filter.
  231.      *
  232.      * After filters are run after the controller has been executed.
  233.      *
  234.      * @param mixed $callback After filter callback
  235.      * @param int   $priority The higher this value, the earlier an event
  236.      *                        listener will be triggered in the chain (defaults to 0)
  237.      */
  238.     public function after($callback$priority 0)
  239.     {
  240.         $app $this;
  241.         $this->on(KernelEvents::RESPONSE, function (FilterResponseEvent $event) use ($callback$app) {
  242.             if (!$event->isMasterRequest()) {
  243.                 return;
  244.             }
  245.             $response call_user_func($app['callback_resolver']->resolveCallback($callback), $event->getRequest(), $event->getResponse(), $app);
  246.             if ($response instanceof Response) {
  247.                 $event->setResponse($response);
  248.             } elseif (null !== $response) {
  249.                 throw new \RuntimeException('An after middleware returned an invalid response value. Must return null or an instance of Response.');
  250.             }
  251.         }, $priority);
  252.     }
  253.     /**
  254.      * Registers a finish filter.
  255.      *
  256.      * Finish filters are run after the response has been sent.
  257.      *
  258.      * @param mixed $callback Finish filter callback
  259.      * @param int   $priority The higher this value, the earlier an event
  260.      *                        listener will be triggered in the chain (defaults to 0)
  261.      */
  262.     public function finish($callback$priority 0)
  263.     {
  264.         $app $this;
  265.         $this->on(KernelEvents::TERMINATE, function (PostResponseEvent $event) use ($callback$app) {
  266.             call_user_func($app['callback_resolver']->resolveCallback($callback), $event->getRequest(), $event->getResponse(), $app);
  267.         }, $priority);
  268.     }
  269.     /**
  270.      * Aborts the current request by sending a proper HTTP error.
  271.      *
  272.      * @param int    $statusCode The HTTP status code
  273.      * @param string $message    The status message
  274.      * @param array  $headers    An array of HTTP headers
  275.      */
  276.     public function abort($statusCode$message '', array $headers = [])
  277.     {
  278.         throw new HttpException($statusCode$messagenull$headers);
  279.     }
  280.     /**
  281.      * Registers an error handler.
  282.      *
  283.      * Error handlers are simple callables which take a single Exception
  284.      * as an argument. If a controller throws an exception, an error handler
  285.      * can return a specific response.
  286.      *
  287.      * When an exception occurs, all handlers will be called, until one returns
  288.      * something (a string or a Response object), at which point that will be
  289.      * returned to the client.
  290.      *
  291.      * For this reason you should add logging handlers before output handlers.
  292.      *
  293.      * @param mixed $callback Error handler callback, takes an Exception argument
  294.      * @param int   $priority The higher this value, the earlier an event
  295.      *                        listener will be triggered in the chain (defaults to -8)
  296.      */
  297.     public function error($callback$priority = -8)
  298.     {
  299.         $this->on(KernelEvents::EXCEPTION, new ExceptionListenerWrapper($this$callback), $priority);
  300.     }
  301.     /**
  302.      * Registers a view handler.
  303.      *
  304.      * View handlers are simple callables which take a controller result and the
  305.      * request as arguments, whenever a controller returns a value that is not
  306.      * an instance of Response. When this occurs, all suitable handlers will be
  307.      * called, until one returns a Response object.
  308.      *
  309.      * @param mixed $callback View handler callback
  310.      * @param int   $priority The higher this value, the earlier an event
  311.      *                        listener will be triggered in the chain (defaults to 0)
  312.      */
  313.     public function view($callback$priority 0)
  314.     {
  315.         $this->on(KernelEvents::VIEW, new ViewListenerWrapper($this$callback), $priority);
  316.     }
  317.     /**
  318.      * Flushes the controller collection.
  319.      */
  320.     public function flush()
  321.     {
  322.         $this['routes']->addCollection($this['controllers']->flush());
  323.     }
  324.     /**
  325.      * Redirects the user to another URL.
  326.      *
  327.      * @param string $url    The URL to redirect to
  328.      * @param int    $status The status code (302 by default)
  329.      *
  330.      * @return RedirectResponse
  331.      */
  332.     public function redirect($url$status 302)
  333.     {
  334.         return new RedirectResponse($url$status);
  335.     }
  336.     /**
  337.      * Creates a streaming response.
  338.      *
  339.      * @param mixed $callback A valid PHP callback
  340.      * @param int   $status   The response status code
  341.      * @param array $headers  An array of response headers
  342.      *
  343.      * @return StreamedResponse
  344.      */
  345.     public function stream($callback null$status 200, array $headers = [])
  346.     {
  347.         return new StreamedResponse($callback$status$headers);
  348.     }
  349.     /**
  350.      * Escapes a text for HTML.
  351.      *
  352.      * @param string $text         The input text to be escaped
  353.      * @param int    $flags        The flags (@see htmlspecialchars)
  354.      * @param string $charset      The charset
  355.      * @param bool   $doubleEncode Whether to try to avoid double escaping or not
  356.      *
  357.      * @return string Escaped text
  358.      */
  359.     public function escape($text$flags ENT_COMPAT$charset null$doubleEncode true)
  360.     {
  361.         return htmlspecialchars($text$flags$charset ?: $this['charset'], $doubleEncode);
  362.     }
  363.     /**
  364.      * Convert some data into a JSON response.
  365.      *
  366.      * @param mixed $data    The response data
  367.      * @param int   $status  The response status code
  368.      * @param array $headers An array of response headers
  369.      *
  370.      * @return JsonResponse
  371.      */
  372.     public function json($data = [], $status 200, array $headers = [])
  373.     {
  374.         return new JsonResponse($data$status$headers);
  375.     }
  376.     /**
  377.      * Sends a file.
  378.      *
  379.      * @param \SplFileInfo|string $file               The file to stream
  380.      * @param int                 $status             The response status code
  381.      * @param array               $headers            An array of response headers
  382.      * @param null|string         $contentDisposition The type of Content-Disposition to set automatically with the filename
  383.      *
  384.      * @return BinaryFileResponse
  385.      */
  386.     public function sendFile($file$status 200, array $headers = [], $contentDisposition null)
  387.     {
  388.         return new BinaryFileResponse($file$status$headerstrue$contentDisposition);
  389.     }
  390.     /**
  391.      * Mounts controllers under the given route prefix.
  392.      *
  393.      * @param string                                                    $prefix      The route prefix
  394.      * @param ControllerCollection|callable|ControllerProviderInterface $controllers A ControllerCollection, a callable, or a ControllerProviderInterface instance
  395.      *
  396.      * @return Application
  397.      *
  398.      * @throws \LogicException
  399.      */
  400.     public function mount($prefix$controllers)
  401.     {
  402.         if ($controllers instanceof ControllerProviderInterface) {
  403.             $connectedControllers $controllers->connect($this);
  404.             if (!$connectedControllers instanceof ControllerCollection) {
  405.                 throw new \LogicException(sprintf('The method "%s::connect" must return a "ControllerCollection" instance. Got: "%s"'get_class($controllers), is_object($connectedControllers) ? get_class($connectedControllers) : gettype($connectedControllers)));
  406.             }
  407.             $controllers $connectedControllers;
  408.         } elseif (!$controllers instanceof ControllerCollection && !is_callable($controllers)) {
  409.             throw new \LogicException('The "mount" method takes either a "ControllerCollection" instance, "ControllerProviderInterface" instance, or a callable.');
  410.         }
  411.         $this['controllers']->mount($prefix$controllers);
  412.         return $this;
  413.     }
  414.     /**
  415.      * Handles the request and delivers the response.
  416.      *
  417.      * @param Request|null $request Request to process
  418.      */
  419.     public function run(Request $request null)
  420.     {
  421.         if (null === $request) {
  422.             $request Request::createFromGlobals();
  423.         }
  424.         $response $this->handle($request);
  425.         $response->send();
  426.         $this->terminate($request$response);
  427.     }
  428.     /**
  429.      * {@inheritdoc}
  430.      *
  431.      * If you call this method directly instead of run(), you must call the
  432.      * terminate() method yourself if you want the finish filters to be run.
  433.      */
  434.     public function handle(Request $request$type HttpKernelInterface::MASTER_REQUEST$catch true)
  435.     {
  436.         if (!$this->booted) {
  437.             $this->boot();
  438.         }
  439.         $this->flush();
  440.         return $this['kernel']->handle($request$type$catch);
  441.     }
  442.     /**
  443.      * {@inheritdoc}
  444.      */
  445.     public function terminate(Request $requestResponse $response)
  446.     {
  447.         $this['kernel']->terminate($request$response);
  448.     }
  449. }