1<?php
2
3/*
4 * This file is part of the Silex framework.
5 *
6 * (c) Fabien Potencier <fabien@symfony.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace Silex\Provider;
13
14use Symfony\Bundle\WebProfilerBundle\Controller\ExceptionController;
15use Symfony\Bundle\WebProfilerBundle\Controller\RouterController;
16use Symfony\Bundle\WebProfilerBundle\Controller\ProfilerController;
17use Symfony\Bundle\WebProfilerBundle\EventListener\WebDebugToolbarListener;
18use Symfony\Bundle\WebProfilerBundle\Twig\WebProfilerExtension;
19use Symfony\Component\Form\Extension\DataCollector\FormDataCollector;
20use Symfony\Component\Form\Extension\DataCollector\FormDataExtractor;
21use Symfony\Component\Form\Extension\DataCollector\Proxy\ResolvedTypeFactoryDataCollectorProxy;
22use Symfony\Component\Form\Extension\DataCollector\Type\DataCollectorTypeExtension;
23use Symfony\Component\HttpKernel\Profiler\Profiler;
24use Symfony\Component\HttpKernel\EventListener\ProfilerListener;
25use Symfony\Component\HttpKernel\Profiler\FileProfilerStorage;
26use Symfony\Component\HttpKernel\DataCollector\ConfigDataCollector;
27use Symfony\Component\HttpKernel\DataCollector\ExceptionDataCollector;
28use Symfony\Component\HttpKernel\DataCollector\RequestDataCollector;
29use Symfony\Component\HttpKernel\DataCollector\RouterDataCollector;
30use Symfony\Component\HttpKernel\DataCollector\MemoryDataCollector;
31use Symfony\Component\HttpKernel\DataCollector\TimeDataCollector;
32use Symfony\Component\HttpKernel\DataCollector\LoggerDataCollector;
33use Symfony\Component\HttpKernel\DataCollector\EventDataCollector;
34use Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher;
35use Symfony\Component\Stopwatch\Stopwatch;
36use Symfony\Bridge\Twig\Extension\CodeExtension;
37use Silex\Application;
38use Silex\ServiceProviderInterface;
39use Silex\ControllerProviderInterface;
40use Silex\ServiceControllerResolver;
41
42/**
43 * Symfony Web Profiler provider.
44 *
45 * @author Fabien Potencier <fabien@symfony.com>
46 */
47class WebProfilerServiceProvider implements ServiceProviderInterface, ControllerProviderInterface
48{
49    public function register(Application $app)
50    {
51        $app['profiler.mount_prefix'] = '/_profiler';
52        $app['dispatcher'] = $app->share($app->extend('dispatcher', function ($dispatcher, $app) {
53            return new TraceableEventDispatcher($dispatcher, $app['stopwatch'], $app['logger']);
54        }));
55
56        $app['data_collector.templates'] = array(
57            array('config',    '@WebProfiler/Collector/config.html.twig'),
58            array('request',   '@WebProfiler/Collector/request.html.twig'),
59            array('exception', '@WebProfiler/Collector/exception.html.twig'),
60            array('events',    '@WebProfiler/Collector/events.html.twig'),
61            array('logger',    '@WebProfiler/Collector/logger.html.twig'),
62            array('time',      '@WebProfiler/Collector/time.html.twig'),
63            array('router',    '@WebProfiler/Collector/router.html.twig'),
64            array('memory',    '@WebProfiler/Collector/memory.html.twig'),
65            array('form',      '@WebProfiler/Collector/form.html.twig'),
66        );
67
68        $app['data_collectors'] = $app->share(function ($app) {
69            return array(
70                'config'    => $app->share(function ($app) { return new ConfigDataCollector(); }),
71                'request'   => $app->share(function ($app) { return new RequestDataCollector(); }),
72                'exception' => $app->share(function ($app) { return new ExceptionDataCollector(); }),
73                'events'    => $app->share(function ($app) { return new EventDataCollector($app['dispatcher']); }),
74                'logger'    => $app->share(function ($app) { return new LoggerDataCollector($app['logger']); }),
75                'time'      => $app->share(function ($app) { return new TimeDataCollector(null, $app['stopwatch']); }),
76                'router'    => $app->share(function ($app) { return new RouterDataCollector(); }),
77                'memory'    => $app->share(function ($app) { return new MemoryDataCollector(); }),
78            );
79        });
80
81        if (isset($app['form.resolved_type_factory']) && class_exists('\Symfony\Component\Form\Extension\DataCollector\FormDataCollector')) {
82            $app['data_collectors.form.extractor'] = $app->share(function () { return new FormDataExtractor(); });
83
84            $app['data_collectors'] = $app->share($app->extend('data_collectors', function ($collectors, $app) {
85                $collectors['form'] = $app->share(function ($app) { return new FormDataCollector($app['data_collectors.form.extractor']); });
86
87                return $collectors;
88            }));
89
90            $app['form.resolved_type_factory'] = $app->share($app->extend('form.resolved_type_factory', function ($factory, $app) {
91                return new ResolvedTypeFactoryDataCollectorProxy($factory, $app['data_collectors']['form']($app));
92            }));
93
94            $app['form.type.extensions'] = $app->share($app->extend('form.type.extensions', function ($extensions, $app) {
95                $extensions[] = new DataCollectorTypeExtension($app['data_collectors']['form']($app));
96
97                return $extensions;
98            }));
99        }
100
101        $app['web_profiler.controller.profiler'] = $app->share(function ($app) {
102            return new ProfilerController($app['url_generator'], $app['profiler'], $app['twig'], $app['data_collector.templates'], $app['web_profiler.debug_toolbar.position']);
103        });
104
105        $app['web_profiler.controller.router'] = $app->share(function ($app) {
106            return new RouterController($app['profiler'], $app['twig'], isset($app['url_matcher']) ? $app['url_matcher'] : null, $app['routes']);
107        });
108
109        $app['web_profiler.controller.exception'] = $app->share(function ($app) {
110            return new ExceptionController($app['profiler'], $app['twig'], $app['debug']);
111        });
112
113        $app['web_profiler.toolbar.listener'] = $app->share(function ($app) {
114            return new WebDebugToolbarListener($app['twig'], $app['web_profiler.debug_toolbar.intercept_redirects'], $app['web_profiler.debug_toolbar.position'], $app['url_generator']);
115        });
116
117        $app['profiler'] = $app->share(function ($app) {
118            $profiler = new Profiler($app['profiler.storage'], $app['logger']);
119
120            foreach ($app['data_collectors'] as $collector) {
121                $profiler->add($collector($app));
122            }
123
124            return $profiler;
125        });
126
127        $app['profiler.storage'] = $app->share(function ($app) {
128            return new FileProfilerStorage('file:'.$app['profiler.cache_dir']);
129        });
130
131        $app['profiler.request_matcher'] = null;
132        $app['profiler.only_exceptions'] = false;
133        $app['profiler.only_master_requests'] = false;
134        $app['web_profiler.debug_toolbar.enable'] = true;
135        $app['web_profiler.debug_toolbar.position'] = 'bottom';
136        $app['web_profiler.debug_toolbar.intercept_redirects'] = false;
137
138        $app['profiler.listener'] = $app->share(function ($app) {
139            return new ProfilerListener(
140                $app['profiler'],
141                $app['profiler.request_matcher'],
142                $app['profiler.only_exceptions'],
143                $app['profiler.only_master_requests']
144            );
145        });
146
147        $app['stopwatch'] = $app->share(function () {
148            return new Stopwatch();
149        });
150
151        $app['code.file_link_format'] = null;
152
153        $app['twig'] = $app->share($app->extend('twig', function ($twig, $app) {
154            $twig->addExtension(new CodeExtension($app['code.file_link_format'], '', $app['charset']));
155
156            if (class_exists('\Symfony\Bundle\WebProfilerBundle\Twig\WebProfilerExtension')) {
157                $twig->addExtension(new WebProfilerExtension());
158            }
159
160            return $twig;
161        }));
162
163        $app['twig.loader.filesystem'] = $app->share($app->extend('twig.loader.filesystem', function ($loader, $app) {
164            $loader->addPath($app['profiler.templates_path'], 'WebProfiler');
165
166            return $loader;
167        }));
168
169        $app['profiler.templates_path'] = function () {
170            $r = new \ReflectionClass('Symfony\Bundle\WebProfilerBundle\EventListener\WebDebugToolbarListener');
171
172            return dirname(dirname($r->getFileName())).'/Resources/views';
173        };
174    }
175
176    public function connect(Application $app)
177    {
178        if (!$app['resolver'] instanceof ServiceControllerResolver) {
179            // using RuntimeException crashes PHP?!
180            throw new \LogicException('You must enable the ServiceController service provider to be able to use the WebProfiler.');
181        }
182
183        $controllers = $app['controllers_factory'];
184
185        $controllers->get('/router/{token}', 'web_profiler.controller.router:panelAction')->bind('_profiler_router');
186        $controllers->get('/exception/{token}.css', 'web_profiler.controller.exception:cssAction')->bind('_profiler_exception_css');
187        $controllers->get('/exception/{token}', 'web_profiler.controller.exception:showAction')->bind('_profiler_exception');
188        $controllers->get('/search', 'web_profiler.controller.profiler:searchAction')->bind('_profiler_search');
189        $controllers->get('/search_bar', 'web_profiler.controller.profiler:searchBarAction')->bind('_profiler_search_bar');
190        $controllers->get('/purge', 'web_profiler.controller.profiler:purgeAction')->bind('_profiler_purge');
191        $controllers->get('/info/{about}', 'web_profiler.controller.profiler:infoAction')->bind('_profiler_info');
192        $controllers->get('/phpinfo', 'web_profiler.controller.profiler:phpinfoAction')->bind('_profiler_phpinfo');
193        $controllers->get('/{token}/search/results', 'web_profiler.controller.profiler:searchResultsAction')->bind('_profiler_search_results');
194        $controllers->get('/{token}', 'web_profiler.controller.profiler:panelAction')->bind('_profiler');
195        $controllers->get('/wdt/{token}', 'web_profiler.controller.profiler:toolbarAction')->bind('_wdt');
196        $controllers->get('/', 'web_profiler.controller.profiler:homeAction')->bind('_profiler_home');
197
198        return $controllers;
199    }
200
201    public function boot(Application $app)
202    {
203        $dispatcher = $app['dispatcher'];
204
205        $dispatcher->addSubscriber($app['profiler.listener']);
206
207        if ($app['web_profiler.debug_toolbar.enable']) {
208            $dispatcher->addSubscriber($app['web_profiler.toolbar.listener']);
209        }
210
211        $dispatcher->addSubscriber($app['profiler']->get('request'));
212        $app->mount($app['profiler.mount_prefix'], $this->connect($app));
213    }
214}
215