diff --git a/config/debugbar.php b/config/debugbar.php index 0872e870..9328fcf7 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -61,6 +61,17 @@ 'capture_ajax' => true, + /* + |-------------------------------------------------------------------------- + | Clockwork integration + |-------------------------------------------------------------------------- + | + | The Debugbar can emulate the Clockwork headers, so you can use the Chrome + | Extension, without the server-side code. It uses Debugbar collectors instead. + | + */ + 'clockwork' => false, + /* |-------------------------------------------------------------------------- | DataCollectors diff --git a/src/Controllers/OpenHandlerController.php b/src/Controllers/OpenHandlerController.php index 7135727f..479d8828 100644 --- a/src/Controllers/OpenHandlerController.php +++ b/src/Controllers/OpenHandlerController.php @@ -1,5 +1,6 @@ 'get', + 'id' => $id, + ]; + + $debugbar = $this->debugbar; + + if (!$debugbar->isEnabled()) { + $this->app->abort('500', 'Debugbar is not enabled'); + } + + $openHandler = new OpenHandler($debugbar); + + $data = $openHandler->handle($request, false, false); + + // Convert to Clockwork + $converter = new Converter(); + $output = $converter->convert(json_decode($data, true)); + + return response()->json($output); + } } diff --git a/src/LaravelDebugbar.php b/src/LaravelDebugbar.php index af3285d3..9ba21946 100644 --- a/src/LaravelDebugbar.php +++ b/src/LaravelDebugbar.php @@ -20,6 +20,7 @@ use DebugBar\DataCollector\PhpInfoCollector; use DebugBar\DataCollector\RequestDataCollector; use DebugBar\DataCollector\TimeDataCollector; +use Barryvdh\Debugbar\Support\Clockwork\ClockworkCollector; use DebugBar\DebugBar; use DebugBar\Storage\PdoStorage; use DebugBar\Storage\RedisStorage; @@ -511,6 +512,23 @@ public function modifyResponse($request, $response) } } + if ($app['config']->get('debugbar.clockwork')) { + + try { + $this->addCollector(new ClockworkCollector($request, $response, $sessionManager)); + } catch (\Exception $e) { + $this->addException( + new Exception( + 'Cannot add ClockworkCollector to Laravel Debugbar: ' . $e->getMessage(), + $e->getCode(), + $e + ) + ); + } + + $this->addClockworkHeaders($response); + } + if ($response->isRedirection()) { try { $this->stackData(); @@ -545,6 +563,7 @@ public function modifyResponse($request, $response) } } + // Stop further rendering (on subrequests etc) $this->disable(); @@ -821,4 +840,11 @@ protected function selectStorage(DebugBar $debugbar) $debugbar->setStorage($storage); } } + + protected function addClockworkHeaders($response) + { + $response->headers->set('X-Clockwork-Id', $this->getCurrentRequestId(), true); + $response->headers->set('X-Clockwork-Version', 1, true); + $response->headers->set('X-Clockwork-Path','/_debugbar/clockwork/', true); + } } diff --git a/src/LumenServiceProvider.php b/src/LumenServiceProvider.php index 86cf100c..70d63690 100644 --- a/src/LumenServiceProvider.php +++ b/src/LumenServiceProvider.php @@ -75,6 +75,11 @@ public function boot() 'uses' => 'OpenHandlerController@handle', 'as' => 'debugbar.openhandler', ]); + + $router->get('clockwork/{id}', [ + 'uses' => 'OpenHandlerController@clockwork', + 'as' => 'debugbar.clockwork', + ]); $router->get('assets/stylesheets', [ 'uses' => 'AssetController@css', diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index b7bbfb25..1cc2e0e5 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -73,6 +73,11 @@ public function boot() 'uses' => 'OpenHandlerController@handle', 'as' => 'debugbar.openhandler', ]); + + $router->get('clockwork/{id}', [ + 'uses' => 'OpenHandlerController@clockwork', + 'as' => 'debugbar.clockwork', + ]); $router->get('assets/stylesheets', [ 'uses' => 'AssetController@css', diff --git a/src/Support/Clockwork/ClockworkCollector.php b/src/Support/Clockwork/ClockworkCollector.php new file mode 100644 index 00000000..3c421c19 --- /dev/null +++ b/src/Support/Clockwork/ClockworkCollector.php @@ -0,0 +1,91 @@ + + * + */ +class ClockworkCollector extends DataCollector implements DataCollectorInterface, Renderable +{ + /** @var \Symfony\Component\HttpFoundation\Request $request */ + protected $request; + /** @var \Symfony\Component\HttpFoundation\Request $response */ + protected $response; + /** @var \Symfony\Component\HttpFoundation\Session\SessionInterface $session */ + protected $session; + + /** + * Create a new SymfonyRequestCollector + * + * @param \Symfony\Component\HttpFoundation\Request $request + * @param \Symfony\Component\HttpFoundation\Request $response + * @param \Symfony\Component\HttpFoundation\Session\SessionInterface $session + */ + public function __construct($request, $response, $session = null) + { + $this->request = $request; + $this->response = $response; + $this->session = $session; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'clockwork'; + } + + /** + * {@inheritDoc} + */ + public function getWidgets() + { + return null; + } + + /** + * {@inheritdoc} + */ + public function collect() + { + $request = $this->request; + $response = $this->response; + + $data = array( + 'getData' => $request->query->all(), + 'postData' => $request->request->all(), + 'headers' => $request->headers->all(), + 'cookies' => $request->cookies->all(), + 'uri' => $request->getRequestUri(), + 'method' => $request->getMethod(), + 'responseStatus' => $response->getStatusCode(), + ); + + if ($this->session) { + $sessionAttributes = array(); + foreach ($this->session->all() as $key => $value) { + $sessionAttributes[$key] = $value; + } + $data['sessionData'] = $sessionAttributes; + } + + if (isset($data['postData']['php-auth-pw'])) { + $data['postData']['php-auth-pw'] = '******'; + } + + if (isset($data['postData']['PHP_AUTH_PW'])) { + $data['postData']['PHP_AUTH_PW'] = '******'; + } + + return $data; + } + +} diff --git a/src/Support/Clockwork/Converter.php b/src/Support/Clockwork/Converter.php new file mode 100644 index 00000000..e6473d97 --- /dev/null +++ b/src/Support/Clockwork/Converter.php @@ -0,0 +1,135 @@ + $meta['id'], + 'method' => $meta['method'], + 'uri' => $meta['uri'], + 'time' => $meta['utime'], + 'headers' => [], + 'cookies' => [], + 'emailsData' => [], + 'getData' => [], + 'log' => [], + 'postData' => [], + 'sessionData' => [], + 'timelineData' => [], + 'viewsData' => [], + 'controller' => null, + 'responseTime' => null, + 'responseStatus' => null, + 'responseDuration' => 0, + ]; + + if (isset($data['clockwork'])) { + $output = array_merge($output, $data['clockwork']); + } + + if (isset($data['time'])) { + $time = $data['time']; + $output['time'] = $time['start']; + $output['responseTime'] = $time['end']; + $output['responseDuration'] = $time['duration'] * 1000; + foreach($time['measures'] as $measure) { + $output['timelineData'][] = [ + 'data' => [], + 'description' => $measure['label'], + 'duration' => $measure['duration'] * 1000, + 'end' => $measure['end'], + 'start' => $measure['start'], + 'relative_start' => $measure['start'] - $time['start'], + ]; + } + } + + if (isset($data['route'])) { + $route = $data['route']; + + $controller = null; + if (isset($route['controller'])) { + $controller = $route['controller']; + } elseif (isset($route['uses'])) { + $controller = $route['uses']; + } + + $output['controller'] = $controller; + + list($method, $uri) = explode(' ', $route['uri'], 2); + + $output['routes'][] = [ + 'action' => $controller, + 'after' => isset($route['after']) ? $route['after'] : null, + 'before' => isset($route['before']) ? $route['before'] : null, + 'method' => $method, + 'name' => isset($route['as']) ? $route['as'] : null, + 'uri' => $uri, + ]; + } + + if (isset($data['messages'])) { + foreach($data['messages']['messages'] as $message) { + $output['log'][] = [ + 'message' => $message['message'], + 'time' => $message['time'], + 'level' => $message['label'], + ]; + } + } + + if (isset($data['queries'])) { + $queries = $data['queries']; + foreach($queries['statements'] as $statement){ + $output['databaseQueries'][] = [ + 'query' => $statement['sql'], + 'bindings' => $statement['params'], + 'duration' => $statement['duration'] * 1000, + 'connection' => $statement['connection'] + ]; + } + + $output['databaseDuration'] = $queries['accumulated_duration'] * 1000; + } + + if (isset($data['views'])) { + foreach ($data['views']['templates'] as $view) { + $output['viewsData'][] = [ + 'description' => 'Rendering a view', + 'duration' => 0, + 'end' => 0, + 'start' => 0, + 'data' => [ + 'name' => $view['name'], + 'data' => $view['params'], + ], + ]; + } + } + + if (isset($data['swiftmailer_mails'])) { + foreach($data['swiftmailer_mails']['mails'] as $mail) { + $output['emailsData'][] = [ + 'data' => [ + 'to' => $mail['to'], + 'subject' => $mail['subject'], + 'headers' => isset($mail['headers']) ? explode("\n", $mail['headers']) : null, + ], + ]; + } + } + + return $output; + } + +}