updated core and modules
This commit is contained in:
@@ -1,28 +1,92 @@
|
||||
!! README.md is a work in progress !!
|
||||
|
||||
#Dependencies:
|
||||
# Dependencies
|
||||
|
||||
- d3.js: Webprofiler module requires D3 library 3.x (not 4.x) to render data.
|
||||
Download https://github.com/mbostock/d3 into /libraries/d3/d3.min.js
|
||||
|
||||
- highlight.js: Webprofiler module requires highlight library to syntax highlight collected queries.
|
||||
Download http://highlightjs.org into /libraries/highlight
|
||||
|
||||
#IDE link:
|
||||
- highlight.js: Webprofiler module requires highlight 9.7.x library to syntax highlight collected queries.
|
||||
|
||||
Every class name discovered while profiling (controller class, event class) are linked to an url for directly open in
|
||||
an IDE, you can configure the url of those link based on the IDE you are using:
|
||||
## Install using Composer (recommended)
|
||||
|
||||
- Sublime text (2 and 3): see https://github.com/dhoulb/subl for Mac OS X
|
||||
- Textmate: should be supported by default, use txmt://open?url=file://@file&line=@line as link
|
||||
- PhpStorm 8+: use phpstorm://open?file=@file&line=@line as link
|
||||
If you use Composer to manage dependencies, edit `/composer.json` as follows.
|
||||
|
||||
#Timeline:
|
||||
1\. Run `composer require --prefer-dist composer/installers` to ensure that you have the `composer/installers` package installed. This package facilitates the installation of packages into directories other than `/vendor` (e.g. `/libraries`) using Composer.
|
||||
|
||||
Now it is possible to also collect the time needed to instantiate every single service used in a request, to make it
|
||||
work you need to add these two lines to settings.php (or, event better, to settings.local.php):
|
||||
2\. Add the following to the "installer-paths" section of `composer.json`:
|
||||
|
||||
```
|
||||
"libraries/{$name}": ["type:drupal-library"],
|
||||
```
|
||||
|
||||
3\. Add the following to the "repositories" section of `composer.json`:
|
||||
|
||||
```
|
||||
"d3": {
|
||||
"type": "package",
|
||||
"package": {
|
||||
"name": "d3/d3",
|
||||
"version": "v3.5.17",
|
||||
"type": "drupal-library",
|
||||
"dist": {
|
||||
"url": "https://github.com/d3/d3/archive/v3.5.17.zip",
|
||||
"type": "zip"
|
||||
}
|
||||
}
|
||||
},
|
||||
"highlightjs": {
|
||||
"type": "package",
|
||||
"package": {
|
||||
"name": "components/highlightjs",
|
||||
"version": "9.7.0",
|
||||
"type": "drupal-library",
|
||||
"dist": {
|
||||
"url": "https://github.com/components/highlightjs/archive/9.7.0.zip",
|
||||
"type": "zip"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
4\. Run `composer require --prefer-dist d3/d3:3.5.* components/highlightjs:9.7.*` - you should find that new directories have been created
|
||||
under `/libraries`
|
||||
|
||||
## Install manually
|
||||
|
||||
- d3.js:
|
||||
|
||||
- Create a `/libraries/d3/` directory below your Drupal root directory
|
||||
- Download https://d3js.org/d3.v3.min.js
|
||||
- Rename it to `/libraries/d3/d3.min.js`
|
||||
|
||||
For further details on how to obtain D3.js, see https://github.com/d3/d3/
|
||||
|
||||
- highlight.js:
|
||||
|
||||
- Create `/libraries/highlightjs/` directory below your Drupal root directory
|
||||
- Download the library and CSS from http://highlightjs.org into it
|
||||
|
||||
# IDE link
|
||||
|
||||
Each class name discovered while profiling (controller class, event class) is specially linked to open the class in
|
||||
an IDE. You can configure the URLs for these links to work for your IDE.
|
||||
|
||||
## Sublime text (2 and 3) - macOS
|
||||
See https://github.com/dhoulb/subl
|
||||
|
||||
## Textmate
|
||||
Use txmt://open?url=file://@file&line=@line
|
||||
|
||||
## PhpStorm 8+
|
||||
Use phpstorm://open?file=@file&line=@line
|
||||
|
||||
# Timeline
|
||||
|
||||
It is also possible to collect the time needed to instantiate every single service used in a request.
|
||||
|
||||
Add the following two lines to `settings.php` (or, even better, to `settings.local.php`):
|
||||
|
||||
```
|
||||
$class_loader->addPsr4('Drupal\\webprofiler\\', [ __DIR__ . '/../../modules/contrib/devel/webprofiler/src']);
|
||||
$settings['container_base_class'] = '\Drupal\webprofiler\DependencyInjection\TraceableContainer';
|
||||
```
|
||||
|
||||
Check if the path from the Webprofiler module in your `settings.php` file matches the location of the installed Webprofiler module in your project.
|
||||
|
||||
@@ -2,17 +2,14 @@
|
||||
"name": "drupal/webprofiler",
|
||||
"description": "Drupal Web Profiler.",
|
||||
"type": "drupal-module",
|
||||
"license": "GPL-2.0+",
|
||||
"require": {
|
||||
"oomphinc/composer-installers-extender" : "v1.1.*",
|
||||
"mbostock/d3": "v3.5.*",
|
||||
"components/highlightjs": "v9.5.*",
|
||||
"symfony/stopwatch": "2.4.*"
|
||||
},
|
||||
"extra": {
|
||||
"installer-types": ["library", "component"],
|
||||
"installer-paths": {
|
||||
"libraries/{$name}": ["mbostock/d3", "components/highlightjs"]
|
||||
"authors": [
|
||||
{
|
||||
"name": "Luca Lusso",
|
||||
"email": "lussoluca@gmail.com",
|
||||
"homepage": "https://github.com/lussoluca",
|
||||
"role": "Maintainer"
|
||||
}
|
||||
}
|
||||
],
|
||||
"license": "GPL-2.0+",
|
||||
"require": {}
|
||||
}
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
commands:
|
||||
webprofiler:
|
||||
export:
|
||||
description: Exports Webprofiler profile/s to file.
|
||||
arguments:
|
||||
id: Profile id.
|
||||
options:
|
||||
directory: Destination directory to store exported file/s.
|
||||
messages:
|
||||
success: Succesfully exported to %s
|
||||
exported_count: <info>Exported %s profiles</info>
|
||||
error_writing: Error writing file %s
|
||||
error_no_profile: No profile with id %s
|
||||
progress:
|
||||
exporting: Exporting profiles...
|
||||
archive: Create archive...
|
||||
delete_tmp: Delete temp files...
|
||||
done: Done.
|
||||
list:
|
||||
description: Lists Webprofiler profiles.
|
||||
options:
|
||||
ip: Filter by IP.
|
||||
url: Filter by URL.
|
||||
method: Filter by HTTP method.
|
||||
limit: Limit printed profiles.
|
||||
rows:
|
||||
time: D, m/d/Y - H:i:s
|
||||
header:
|
||||
token: Token
|
||||
ip: IP
|
||||
method: Method
|
||||
url: URL
|
||||
time: Time
|
||||
benchmark:
|
||||
description: Benchmark an url.
|
||||
arguments:
|
||||
url: Url to benchmark.
|
||||
options:
|
||||
runs: Number of runs.
|
||||
file: Save results as file.
|
||||
cache-rebuild: Rebuild cache before start benchmark.
|
||||
messages:
|
||||
not_git: Not in a git repository.
|
||||
error_login: Impossibile to login in the user.
|
||||
progress:
|
||||
cache_rebuild: Rebuilding cache...
|
||||
login: Login user...
|
||||
get: Http request...
|
||||
compute_avg: Compute average...
|
||||
compute_median: Compute median...
|
||||
compute_95percentile: Compute 95 percentile...
|
||||
git_hash: Compute GIT hash...
|
||||
yaml: Generate output...
|
||||
done: Done.
|
||||
@@ -1,54 +0,0 @@
|
||||
commands:
|
||||
webprofiler:
|
||||
export:
|
||||
description: Exportar Webprofiler perfil(es) a un archivo.
|
||||
arguments:
|
||||
id: Profile id.
|
||||
options:
|
||||
directory: Directorio de destiono para almacenar archivo(s) exportados.
|
||||
messages:
|
||||
success: Satisfactoriamente exportado en %s
|
||||
exported_count: <info>Exportados %s perfiles</info>
|
||||
error_writing: Error escribiendo el archivo %s
|
||||
error_no_profile: No fue encontrado un perfil con id %s
|
||||
progress:
|
||||
exporting: Exportando perfiles...
|
||||
archive: Creando archivos...
|
||||
delete_tmp: Borrando archivos temporales...
|
||||
done: Terminado.
|
||||
list:
|
||||
description: Listar perfiles Webprofiler.
|
||||
options:
|
||||
ip: Filtrar por IP.
|
||||
url: Filtrar por URL.
|
||||
method: Filtrar por método HTTP.
|
||||
limit: Limitar perfiles a mostrar.
|
||||
rows:
|
||||
time: D, d/m/Y - H:i:s
|
||||
header:
|
||||
token: Token
|
||||
ip: IP
|
||||
method: Método
|
||||
url: URL
|
||||
time: Fecha
|
||||
benchmark:
|
||||
description: Benchmark una URL.
|
||||
arguments:
|
||||
url: Url para efectuar un benchmark.
|
||||
options:
|
||||
runs: Número de ejecuciones.
|
||||
file: Guardar los resultados en un archivo.
|
||||
cache-rebuild: Reconstruir cache antes de iniciar el benchmark.
|
||||
messages:
|
||||
not_git: No en un repositorio git.
|
||||
error_login: Error ingresando al sistema.
|
||||
progress:
|
||||
cache_rebuild: Reconstruyendo cache...
|
||||
login: Ingresando al sistema...
|
||||
get: Solicitud Http...
|
||||
compute_avg: Calculando promedio...
|
||||
compute_median: Calculando media...
|
||||
compute_95percentile: Calculando percentile 95...
|
||||
git_hash: Calculando GIT hash...
|
||||
yaml: Generando salida...
|
||||
done: Terminado.
|
||||
@@ -0,0 +1,14 @@
|
||||
services:
|
||||
webprofiler.list:
|
||||
class: Drupal\webprofiler\Command\ListCommand
|
||||
tags:
|
||||
- { name: drupal.command }
|
||||
lazy: true
|
||||
webprofiler.export:
|
||||
class: Drupal\webprofiler\Command\ExportCommand
|
||||
tags:
|
||||
- { name: drupal.command }
|
||||
webprofiler.benchmark:
|
||||
class: Drupal\webprofiler\Command\BenchmarkCommand
|
||||
tags:
|
||||
- { name: drupal.command }
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
description: Benchmark an url.
|
||||
arguments:
|
||||
url: Url to benchmark.
|
||||
options:
|
||||
runs: Number of runs.
|
||||
file: Save results as file.
|
||||
cache-rebuild: Rebuild cache before start benchmark.
|
||||
messages:
|
||||
not_git: Not in a git repository.
|
||||
error_login: Impossibile to login in the user.
|
||||
progress:
|
||||
cache_rebuild: Rebuilding cache...
|
||||
login: Login user...
|
||||
get: Http request...
|
||||
compute_avg: Compute average...
|
||||
compute_median: Compute median...
|
||||
compute_95percentile: Compute 95 percentile...
|
||||
git_hash: Compute GIT hash...
|
||||
yaml: Generate output...
|
||||
done: Done.
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
description: Exports Webprofiler profile/s to file.
|
||||
arguments:
|
||||
id: Profile id.
|
||||
options:
|
||||
directory: Destination directory to store exported file/s.
|
||||
messages:
|
||||
success: Succesfully exported to %s
|
||||
exported_count: <info>Exported %s profiles</info>
|
||||
error_writing: Error writing file %s
|
||||
error_no_profile: No profile with id %s
|
||||
progress:
|
||||
exporting: Exporting profiles...
|
||||
archive: Create archive...
|
||||
delete_tmp: Delete temp files...
|
||||
done: Done.
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
description: Lists Webprofiler profiles.
|
||||
options:
|
||||
ip: Filter by IP.
|
||||
url: Filter by URL.
|
||||
method: Filter by HTTP method.
|
||||
limit: Limit printed profiles.
|
||||
rows:
|
||||
time: D, m/d/Y - H:i:s
|
||||
header:
|
||||
token: Token
|
||||
ip: IP
|
||||
method: Method
|
||||
url: URL
|
||||
time: Time
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
description: Benchmark una URL.
|
||||
arguments:
|
||||
url: Url para efectuar un benchmark.
|
||||
options:
|
||||
runs: Número de ejecuciones.
|
||||
file: Guardar los resultados en un archivo.
|
||||
cache-rebuild: Reconstruir cache antes de iniciar el benchmark.
|
||||
messages:
|
||||
not_git: No en un repositorio git.
|
||||
error_login: Error ingresando al sistema.
|
||||
progress:
|
||||
cache_rebuild: Reconstruyendo cache...
|
||||
login: Ingresando al sistema...
|
||||
get: Solicitud Http...
|
||||
compute_avg: Calculando promedio...
|
||||
compute_median: Calculando media...
|
||||
compute_95percentile: Calculando percentile 95...
|
||||
git_hash: Calculando GIT hash...
|
||||
yaml: Generando salida...
|
||||
done: Terminado.
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
description: Exportar Webprofiler perfil(es) a un archivo.
|
||||
arguments:
|
||||
id: Profile id.
|
||||
options:
|
||||
directory: Directorio de destiono para almacenar archivo(s) exportados.
|
||||
messages:
|
||||
success: Satisfactoriamente exportado en %s
|
||||
exported_count: <info>Exportados %s perfiles</info>
|
||||
error_writing: Error escribiendo el archivo %s
|
||||
error_no_profile: No fue encontrado un perfil con id %s
|
||||
progress:
|
||||
exporting: Exportando perfiles...
|
||||
archive: Creando archivos...
|
||||
delete_tmp: Borrando archivos temporales...
|
||||
done: Terminado.
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
description: Listar perfiles Webprofiler.
|
||||
options:
|
||||
ip: Filtrar por IP.
|
||||
url: Filtrar por URL.
|
||||
method: Filtrar por método HTTP.
|
||||
limit: Limitar perfiles a mostrar.
|
||||
rows:
|
||||
time: D, d/m/Y - H:i:s
|
||||
header:
|
||||
token: Token
|
||||
ip: IP
|
||||
method: Método
|
||||
url: URL
|
||||
time: Fecha
|
||||
@@ -9,6 +9,9 @@
|
||||
},
|
||||
|
||||
repl = function replaceAll(string, find, replace) {
|
||||
if (typeof string != 'string') {
|
||||
return '';
|
||||
}
|
||||
return string.replace(new RegExp(escapeRx(find), 'g'), replace);
|
||||
},
|
||||
|
||||
|
||||
@@ -4,12 +4,13 @@ namespace Drupal\webprofiler\Cache;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Cache\CacheTagsInvalidatorInterface;
|
||||
use Drupal\webprofiler\DataCollector\CacheDataCollector;
|
||||
|
||||
/**
|
||||
* Wraps an existing cache backend to track calls to the cache backend.
|
||||
*/
|
||||
class CacheBackendWrapper implements CacheBackendInterface {
|
||||
class CacheBackendWrapper implements CacheBackendInterface, CacheTagsInvalidatorInterface {
|
||||
|
||||
/**
|
||||
* The data collector to register the calls.
|
||||
@@ -55,7 +56,7 @@ class CacheBackendWrapper implements CacheBackendInterface {
|
||||
$cache = $this->cacheBackend->get($cid, $allow_invalid);
|
||||
|
||||
if ($cache) {
|
||||
$cacheCopy = new \StdClass();
|
||||
$cacheCopy = new \stdClass();
|
||||
$cacheCopy->cid = $cache->cid;
|
||||
$cacheCopy->expire = $cache->expire;
|
||||
$cacheCopy->tags = $cache->tags;
|
||||
@@ -81,7 +82,7 @@ class CacheBackendWrapper implements CacheBackendInterface {
|
||||
$this->cacheDataCollector->registerCacheMiss($this->bin, $cid);
|
||||
}
|
||||
else {
|
||||
$cacheCopy = new \StdClass();
|
||||
$cacheCopy = new \stdClass();
|
||||
$cacheCopy->cid = $cache[$cid]->cid;
|
||||
$cacheCopy->expire = $cache[$cid]->expire;
|
||||
$cacheCopy->tags = $cache[$cid]->tags;
|
||||
@@ -121,13 +122,6 @@ class CacheBackendWrapper implements CacheBackendInterface {
|
||||
return $this->cacheBackend->deleteMultiple($cids);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteTags(array $tags) {
|
||||
return $this->cacheBackend->deleteTags($tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@@ -153,7 +147,9 @@ class CacheBackendWrapper implements CacheBackendInterface {
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function invalidateTags(array $tags) {
|
||||
return $this->cacheBackend->invalidateTags($tags);
|
||||
if ($this->cacheBackend instanceof CacheTagsInvalidatorInterface) {
|
||||
$this->cacheBackend->invalidateTags($tags);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -176,4 +172,5 @@ class CacheBackendWrapper implements CacheBackendInterface {
|
||||
public function removeBin() {
|
||||
return $this->cacheBackend->removeBin();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,15 +9,24 @@ use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Drupal\Console\Command\ContainerAwareCommand;
|
||||
use Drupal\Console\Core\Command\Shared\ContainerAwareCommandTrait;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\DomCrawler\Crawler;
|
||||
use Symfony\Component\Process\Process;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use Drupal\Console\Annotations\DrupalCommand;
|
||||
|
||||
/**
|
||||
* Class BenchmarkCommand
|
||||
*
|
||||
* @DrupalCommand (
|
||||
* extension="webprofiler",
|
||||
* extensionType="module"
|
||||
* )
|
||||
*/
|
||||
class BenchmarkCommand extends ContainerAwareCommand {
|
||||
class BenchmarkCommand extends Command {
|
||||
|
||||
use ContainerAwareCommandTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -56,7 +65,7 @@ class BenchmarkCommand extends ContainerAwareCommand {
|
||||
}
|
||||
|
||||
/** @var \Drupal\Core\Http\Client $client */
|
||||
$client = $this->getContainer()->get('http_client');
|
||||
$client = $this->container->get('http_client');
|
||||
|
||||
$progress = new ProgressBar($output, $runs + $steps);
|
||||
$progress->setFormat(' %current%/%max% [%bar%] %percent:3s%% %message%');
|
||||
@@ -152,7 +161,7 @@ class BenchmarkCommand extends ContainerAwareCommand {
|
||||
$token = $response->getHeader('X-Debug-Token');
|
||||
|
||||
/** @var \Drupal\webprofiler\Profiler\Profiler $profiler */
|
||||
$profiler = $this->getContainer()->get('profiler');
|
||||
$profiler = $this->container->get('profiler');
|
||||
|
||||
/** @var \Symfony\Component\HttpKernel\Profiler\Profile $profile */
|
||||
$profile = $profiler->loadProfile($token);
|
||||
|
||||
@@ -2,19 +2,28 @@
|
||||
|
||||
namespace Drupal\webprofiler\Command;
|
||||
|
||||
use Drupal\Console\Command\ContainerAwareCommand;
|
||||
use Drupal\Console\Core\Command\Shared\ContainerAwareCommandTrait;
|
||||
use Drupal\Core\Archiver\ArchiveTar;
|
||||
use Drupal\webprofiler\Profiler\Profiler;
|
||||
use Symfony\Component\Console\Helper\ProgressBar;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Drupal\Console\Annotations\DrupalCommand;
|
||||
|
||||
/**
|
||||
* Class ExportCommand
|
||||
*
|
||||
* @DrupalCommand (
|
||||
* extension="webprofiler",
|
||||
* extensionType="module"
|
||||
* )
|
||||
*/
|
||||
class ExportCommand extends ContainerAwareCommand {
|
||||
class ExportCommand extends Command {
|
||||
|
||||
use ContainerAwareCommandTrait;
|
||||
|
||||
/** @var string */
|
||||
private $filename;
|
||||
@@ -38,7 +47,7 @@ class ExportCommand extends ContainerAwareCommand {
|
||||
$directory = $input->getOption('directory');
|
||||
|
||||
/** @var \Drupal\webprofiler\Profiler\Profiler $profiler */
|
||||
$profiler = $this->getContainer()->get('profiler');
|
||||
$profiler = $this->container->get('profiler');
|
||||
|
||||
try {
|
||||
if ($id) {
|
||||
|
||||
@@ -2,16 +2,25 @@
|
||||
|
||||
namespace Drupal\webprofiler\Command;
|
||||
|
||||
use Drupal\Console\Command\ContainerAwareCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Helper\Table;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Drupal\Console\Core\Command\Shared\ContainerAwareCommandTrait;
|
||||
use Drupal\Console\Annotations\DrupalCommand;
|
||||
|
||||
/**
|
||||
* Class ListCommand
|
||||
**
|
||||
* @DrupalCommand (
|
||||
* extension="webprofiler",
|
||||
* extensionType="module"
|
||||
* )
|
||||
*/
|
||||
class ListCommand extends ContainerAwareCommand {
|
||||
class ListCommand extends Command {
|
||||
|
||||
use ContainerAwareCommandTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -36,7 +45,7 @@ class ListCommand extends ContainerAwareCommand {
|
||||
$limit = $input->getOption('limit');
|
||||
|
||||
/** @var \Drupal\webprofiler\Profiler\Profiler $profiler */
|
||||
$profiler = $this->getContainer()->get('profiler');
|
||||
$profiler = $this->container->get('profiler');
|
||||
$profiles = $profiler->find($ip, $url, $limit, $method, '', '');
|
||||
|
||||
$rows = [];
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
* Class EventPass.
|
||||
*/
|
||||
class EventPass implements CompilerPassInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function process(ContainerBuilder $container) {
|
||||
$definition = $container->findDefinition('http_kernel.basic');
|
||||
$definition->replaceArgument(1, new Reference('webprofiler.debug.controller_resolver'));
|
||||
|
||||
// Replace the regular event_dispatcher service with the debug one.
|
||||
$definition = $container->findDefinition('event_dispatcher');
|
||||
$definition->setPublic(FALSE);
|
||||
$container->setDefinition('webprofiler.debug.event_dispatcher.default', $definition);
|
||||
$container->register('event_dispatcher', 'Drupal\webprofiler\TraceableEventDispatcher')
|
||||
->addArgument(new Reference('webprofiler.debug.event_dispatcher.default'))
|
||||
->addArgument(new Reference('stopwatch'))
|
||||
->setProperty('_serviceId', 'event_dispatcher');
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,6 @@ class ServicePass implements CompilerPassInterface {
|
||||
$inEdges[] = [
|
||||
'id' => $edge->getSourceNode()->getId(),
|
||||
'invalidBehavior' => $edgeValue ? $edgeValue->getInvalidBehavior() : NULL,
|
||||
'strict' => $edgeValue ? $edgeValue->isStrict() : NULL,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -63,7 +62,6 @@ class ServicePass implements CompilerPassInterface {
|
||||
$outEdges[] = [
|
||||
'id' => $edge->getDestNode()->getId(),
|
||||
'invalidBehavior' => $edgeValue ? $edgeValue->getInvalidBehavior() : NULL,
|
||||
'strict' => $edgeValue ? $edgeValue->isStrict() : NULL,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -124,7 +124,7 @@ class BlocksDataCollector extends DataCollector implements DrupalDataCollectorIn
|
||||
/** @var \Drupal\block\BlockInterface $block */
|
||||
foreach ($decorator->getEntities() as $block) {
|
||||
/** @var Block $entity */
|
||||
if ($entity = $storage->load($block->get('id'))) {
|
||||
if (null !== $block && $entity = $storage->load($block->get('id'))) {
|
||||
|
||||
$route = '';
|
||||
if ($entity->hasLinkTemplate('edit-form')) {
|
||||
|
||||
+42
-49
@@ -1,14 +1,12 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Contains \Drupal\webprofiler\DataCollector\DevelDataCollector;
|
||||
*/
|
||||
|
||||
namespace Drupal\webprofiler\DataCollector;
|
||||
|
||||
use Drupal\Core\Menu\MenuLinkTreeInterface;
|
||||
use Drupal\Core\Link;
|
||||
use Drupal\Core\Menu\MenuTreeParameters;
|
||||
use Drupal\Core\Render\Markup;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\devel\Plugin\Menu\DestinationMenuLink;
|
||||
use Drupal\webprofiler\DrupalDataCollectorInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
@@ -21,30 +19,18 @@ class DevelDataCollector extends DataCollector implements DrupalDataCollectorInt
|
||||
|
||||
use StringTranslationTrait, DrupalDataCollectorTrait;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Menu\MenuLinkTreeInterface
|
||||
*/
|
||||
private $menuLinkTree;
|
||||
|
||||
/**
|
||||
* @param \Drupal\Core\Menu\MenuLinkTreeInterface $menuLinkTree
|
||||
*/
|
||||
public function __construct(MenuLinkTreeInterface $menuLinkTree) {
|
||||
$this->menuLinkTree = $menuLinkTree;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Exception $exception = NULL) {
|
||||
$this->data['links'] = $this->develMenuLinks();
|
||||
$this->data['original_url'] = $request->getPathInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLinks() {
|
||||
return $this->data['links'];
|
||||
return $this->develMenuLinks($this->data['original_url']);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,36 +54,6 @@ class DevelDataCollector extends DataCollector implements DrupalDataCollectorInt
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* Array containing Devel Menu links
|
||||
*/
|
||||
protected function develMenuLinks() {
|
||||
$parameters = new MenuTreeParameters();
|
||||
$parameters->setMaxDepth(1)->onlyEnabledLinks();
|
||||
$tree = $this->menuLinkTree->load('devel', $parameters);
|
||||
|
||||
$manipulators = array(
|
||||
array('callable' => 'menu.default_tree_manipulators:checkAccess'),
|
||||
array('callable' => 'menu.default_tree_manipulators:generateIndexAndSort'),
|
||||
);
|
||||
$tree = $this->menuLinkTree->transform($tree, $manipulators);
|
||||
|
||||
$links = array();
|
||||
|
||||
foreach ($tree as $item) {
|
||||
if ($item->access->isAllowed()) {
|
||||
$links[] = array(
|
||||
'title' => $item->link->getTitle(),
|
||||
'description' => $item->link->getDescription(),
|
||||
'url' => $item->link->getUrlObject()->toString(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the collector icon in base64 format.
|
||||
*
|
||||
@@ -107,4 +63,41 @@ class DevelDataCollector extends DataCollector implements DrupalDataCollectorInt
|
||||
public function getIcon() {
|
||||
return 'iVBORw0KGgoAAAANSUhEUgAAABYAAAAcCAYAAABlL09dAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNS4xIE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2RTdCREU2NUVFQUUxMUU1QTc4Q0Q0OEU5RUY0N0YwMyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo2RTdCREU2NkVFQUUxMUU1QTc4Q0Q0OEU5RUY0N0YwMyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjZFN0JERTYzRUVBRTExRTVBNzhDRDQ4RTlFRjQ3RjAzIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjZFN0JERTY0RUVBRTExRTVBNzhDRDQ4RTlFRjQ3RjAzIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+6sNOKAAAAPVJREFUeNpi/P//PwMtABMDjcCowbQ3mNHe3h6XnBUQTwJiTRzy14E4+8CBAyexSbLgsZQbiI2BOBCH/HqoGgZSDYaBDXjk/uMzuBaLzeVI7FV4DN7n4OAAojvRxL+CwvgnkMFG5bj7BUoVv4lUfBWIrxGp9jcoKM4DsQABhZ+A2BPqs61AzEVA/XuQwT1AzElA4Q8g/gJN951EBN13kMFLgJiHgMJvQCwNxOxAvJwIg7+wEBnGv4D4M1TtLyIMBocxMQUyKExnAzEzEHMQof4/C5GxDHJh4qAp3VhpYC4rKCieAbEQlQ1+yzhamY4aTD+DAQIMAFv+MFaJEyYhAAAAAElFTkSuQmCC';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $original_url
|
||||
*
|
||||
* @return array Array containing Devel Menu links
|
||||
* Array containing Devel Menu links
|
||||
*/
|
||||
protected function develMenuLinks($original_url) {
|
||||
// We cannot use injected services here because at this point this
|
||||
// class is deserialized from a storage and not constructed.
|
||||
$menuLinkTreeService = \Drupal::service('menu.link_tree');
|
||||
$rendererService = \Drupal::service('renderer');
|
||||
|
||||
$parameters = new MenuTreeParameters();
|
||||
$parameters->setMaxDepth(1)->onlyEnabledLinks();
|
||||
$tree = $menuLinkTreeService->load('devel', $parameters);
|
||||
|
||||
$manipulators = array(
|
||||
array('callable' => 'menu.default_tree_manipulators:checkAccess'),
|
||||
array('callable' => 'menu.default_tree_manipulators:generateIndexAndSort'),
|
||||
);
|
||||
$tree = $menuLinkTreeService->transform($tree, $manipulators);
|
||||
|
||||
$links = array();
|
||||
|
||||
foreach ($tree as $item) {
|
||||
/** @var DestinationMenuLink $link */
|
||||
$link = $item->link;
|
||||
$renderable = Link::fromTextAndUrl($link->getTitle(), $link->getUrlObject())
|
||||
->toRenderable();
|
||||
$rendered = $rendererService->renderPlain($renderable);
|
||||
$linkString = preg_replace('/\/profiler\/(.*)&/', $original_url . '&', $rendered);
|
||||
$links[] = Markup::create($linkString);;
|
||||
}
|
||||
|
||||
return $links;
|
||||
}
|
||||
}
|
||||
|
||||
+91
-37
@@ -2,69 +2,123 @@
|
||||
|
||||
namespace Drupal\webprofiler\DataCollector;
|
||||
|
||||
use Drupal\webprofiler\DrupalDataCollectorInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Symfony\Component\HttpKernel\DataCollector\EventDataCollector as BaseEventDataCollector;
|
||||
use Drupal\webprofiler\DrupalDataCollectorInterface;
|
||||
use Drupal\webprofiler\EventDispatcher\EventDispatcherTraceableInterface;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
||||
use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface;
|
||||
|
||||
/**
|
||||
* Class EventsDataCollector
|
||||
*/
|
||||
class EventsDataCollector extends BaseEventDataCollector implements DrupalDataCollectorInterface {
|
||||
class EventsDataCollector extends DataCollector implements DrupalDataCollectorInterface, LateDataCollectorInterface {
|
||||
|
||||
use StringTranslationTrait, DrupalDataCollectorTrait;
|
||||
|
||||
/**
|
||||
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
|
||||
*/
|
||||
private $eventDispatcher;
|
||||
|
||||
/**
|
||||
* EventsDataCollector constructor.
|
||||
*
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
|
||||
*/
|
||||
public function __construct(EventDispatcherInterface $event_dispatcher) {
|
||||
$this->eventDispatcher = $event_dispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Exception $exception = NULL) {
|
||||
$this->data = [
|
||||
'called_listeners' => [],
|
||||
'called_listeners_count' => 0,
|
||||
'not_called_listeners' => [],
|
||||
'not_called_listeners_count' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function lateCollect() {
|
||||
if ($this->eventDispatcher instanceof EventDispatcherTraceableInterface) {
|
||||
$countCalled = 0;
|
||||
$calledListeners = $this->eventDispatcher->getCalledListeners();
|
||||
foreach ($calledListeners as &$events) {
|
||||
foreach ($events as &$priority) {
|
||||
foreach ($priority as &$listener) {
|
||||
$countCalled++;
|
||||
$listener['clazz'] = $this->getMethodData($listener['class'], $listener['method']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$countNotCalled = 0;
|
||||
$notCalledListeners = $this->eventDispatcher->getNotCalledListeners();
|
||||
foreach ($notCalledListeners as $events) {
|
||||
foreach ($events as $priority) {
|
||||
foreach ($priority as $listener) {
|
||||
$countNotCalled++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->data = [
|
||||
'called_listeners' => $calledListeners,
|
||||
'called_listeners_count' => $countCalled,
|
||||
'not_called_listeners' => $notCalledListeners,
|
||||
'not_called_listeners_count' => $countNotCalled,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getCalledListeners() {
|
||||
return $this->data['called_listeners'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getNotCalledListeners() {
|
||||
return $this->data['not_called_listeners'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getCalledListenersCount() {
|
||||
return count($this->getCalledListeners());
|
||||
return $this->data['called_listeners_count'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getNotCalledListenersCount() {
|
||||
return count($this->getNotCalledListeners());
|
||||
return $this->data['not_called_listeners_count'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setCalledListeners(array $listeners) {
|
||||
$listeners = $this->computePriority($listeners);
|
||||
$this->data['called_listeners'] = $listeners;
|
||||
public function getName() {
|
||||
return 'events';
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the priority value to the $listeners array.
|
||||
*
|
||||
* @param array $listeners
|
||||
* @return array
|
||||
* @return mixed
|
||||
*/
|
||||
private function computePriority(array $listeners) {
|
||||
foreach ($listeners as &$listener) {
|
||||
foreach ($listener['class']::getSubscribedEvents() as $event => $methods) {
|
||||
|
||||
if (is_string($methods)) {
|
||||
$methods = [[$methods], 0];
|
||||
}
|
||||
else {
|
||||
if (is_string($methods[0])) {
|
||||
$methods = [$methods];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($methods as $method) {
|
||||
if ($listener['event'] === $event) {
|
||||
if ($listener['method'] === $method[0]) {
|
||||
$listener['priority'] = isset($method[1]) ? $method[1] : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $listeners;
|
||||
public function getData() {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,7 +132,7 @@ class EventsDataCollector extends BaseEventDataCollector implements DrupalDataCo
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPanelSummary() {
|
||||
return $this->t('Called listeners: @listeners', ['@listeners' => count($this->getCalledListeners())]);
|
||||
return $this->t('Called listeners: @listeners', ['@listeners' => $this->getCalledListenersCount()]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+16
-9
@@ -63,7 +63,7 @@ class HttpDataCollector extends DataCollector implements DrupalDataCollectorInte
|
||||
'request_target' => $request->getRequestTarget(),
|
||||
'stats' => [
|
||||
'transferTime' => $stats->getTransferTime(),
|
||||
'handlerStats' => $stats->getHandlerStats()
|
||||
'handlerStats' => $stats->getHandlerStats(),
|
||||
],
|
||||
],
|
||||
'response' => [
|
||||
@@ -71,7 +71,7 @@ class HttpDataCollector extends DataCollector implements DrupalDataCollectorInte
|
||||
'status' => $response->getStatusCode(),
|
||||
'headers' => $response->getHeaders(),
|
||||
'protocol' => $response->getProtocolVersion(),
|
||||
]
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ class HttpDataCollector extends DataCollector implements DrupalDataCollectorInte
|
||||
$response = $data['response'];
|
||||
|
||||
$uri = $request->getUri();
|
||||
$this->data['failed'][] = [
|
||||
$failureData = [
|
||||
'request' => [
|
||||
'method' => $request->getMethod(),
|
||||
'uri' => [
|
||||
@@ -97,13 +97,18 @@ class HttpDataCollector extends DataCollector implements DrupalDataCollectorInte
|
||||
'protocol' => $request->getProtocolVersion(),
|
||||
'request_target' => $request->getRequestTarget(),
|
||||
],
|
||||
'response' => [
|
||||
];
|
||||
|
||||
if ($response) {
|
||||
$failureData['response'] = [
|
||||
'phrase' => $response->getReasonPhrase(),
|
||||
'status' => $response->getStatusCode(),
|
||||
'headers' => $response->getHeaders(),
|
||||
'protocol' => $response->getProtocolVersion(),
|
||||
]
|
||||
];
|
||||
];
|
||||
}
|
||||
|
||||
$this->data['failed'][] = $failureData;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,10 +158,12 @@ class HttpDataCollector extends DataCollector implements DrupalDataCollectorInte
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPanelSummary() {
|
||||
return $this->t('Completed @completed, error @error', [
|
||||
return $this->t(
|
||||
'Completed @completed, error @error', [
|
||||
'@completed' => $this->getCompletedRequestsCount(),
|
||||
'@error' => $this->getFailedRequestsCount()
|
||||
]);
|
||||
'@error' => $this->getFailedRequestsCount(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+4
-4
@@ -5,7 +5,7 @@ namespace Drupal\webprofiler\DataCollector;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\webprofiler\DependencyInjection\TraceableContainer;
|
||||
use Drupal\webprofiler\DrupalDataCollectorInterface;
|
||||
use Symfony\Component\DependencyInjection\IntrospectableContainerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
||||
@@ -18,15 +18,15 @@ class ServicesDataCollector extends DataCollector implements DrupalDataCollector
|
||||
use StringTranslationTrait, DrupalDataCollectorTrait;
|
||||
|
||||
/**
|
||||
* @var \Symfony\Component\DependencyInjection\IntrospectableContainerInterface
|
||||
* @var \Symfony\Component\DependencyInjection\ContainerInterface
|
||||
* $container
|
||||
*/
|
||||
private $container;
|
||||
|
||||
/**
|
||||
* @param IntrospectableContainerInterface $container
|
||||
* @param \Symfony\Component\DependencyInjection\ContainerInterface $container
|
||||
*/
|
||||
public function __construct(IntrospectableContainerInterface $container) {
|
||||
public function __construct(ContainerInterface $container) {
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -49,7 +49,7 @@ class TranslationsDataCollector extends DataCollector implements DrupalDataColle
|
||||
$this->data['translations']['translated'] = $this->translation->getTranslated();
|
||||
$this->data['translations']['untranslated'] = $this->translation->getUntranslated();
|
||||
}
|
||||
$data['user_interface_translations_path'] = $this->urlGenerator->generateFromRoute('locale.translate_page');
|
||||
$this->data['user_interface_translations_path'] = $this->urlGenerator->generateFromRoute('locale.translate_page');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+6
-6
@@ -15,42 +15,42 @@ class ShortcutSetStorageDecorator extends ConfigEntityStorageDecorator implement
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function assignUser(ShortcutSetInterface $shortcut_set, $account) {
|
||||
// TODO: Implement assignUser() method.
|
||||
$this->getOriginalObject()->assignUser($shortcut_set, $account);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function unassignUser($account) {
|
||||
// TODO: Implement unassignUser() method.
|
||||
return $this->getOriginalObject()->unassignUser($account);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteAssignedShortcutSets(ShortcutSetInterface $entity) {
|
||||
// TODO: Implement deleteAssignedShortcutSets() method.
|
||||
$this->getOriginalObject()->deleteAssignedShortcutSets($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAssignedToUser($account) {
|
||||
// TODO: Implement getAssignedToUser() method.
|
||||
return $this->getOriginalObject()->getAssignedToUser($account);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function countAssignedUsers(ShortcutSetInterface $shortcut_set) {
|
||||
// TODO: Implement countAssignedUsers() method.
|
||||
return $this->getOriginalObject()->countAssignedUsers($shortcut_set);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefaultSet(AccountInterface $account) {
|
||||
// TODO: Implement getDefaultSet() method.
|
||||
return $this->getOriginalObject()->getDefaultSet($account);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\EventDispatcher;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
interface EventDispatcherTraceableInterface extends EventDispatcherInterface {
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getCalledListeners();
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getNotCalledListeners();
|
||||
|
||||
}
|
||||
+195
@@ -0,0 +1,195 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\EventDispatcher;
|
||||
|
||||
use Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher;
|
||||
use Drupal\webprofiler\Stopwatch;
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
|
||||
/**
|
||||
* Class TraceableEventDispatcher
|
||||
*/
|
||||
class TraceableEventDispatcher extends ContainerAwareEventDispatcher implements EventDispatcherTraceableInterface {
|
||||
|
||||
/**
|
||||
* @var \Drupal\webprofiler\Stopwatch
|
||||
* The stopwatch service.
|
||||
*/
|
||||
protected $stopwatch;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $calledListeners;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $notCalledListeners;
|
||||
|
||||
/**
|
||||
* @param \Drupal\webprofiler\Stopwatch $stopwatch
|
||||
*/
|
||||
public function setStopwatch(Stopwatch $stopwatch) {
|
||||
$this->stopwatch = $stopwatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function dispatch($event_name, Event $event = NULL) {
|
||||
if ($event === NULL) {
|
||||
$event = new Event();
|
||||
}
|
||||
|
||||
$this->preDispatch($event_name, $event);
|
||||
$e = $this->stopwatch->start($event_name, 'section');
|
||||
|
||||
if (isset($this->listeners[$event_name])) {
|
||||
// Sort listeners if necessary.
|
||||
if (isset($this->unsorted[$event_name])) {
|
||||
krsort($this->listeners[$event_name]);
|
||||
unset($this->unsorted[$event_name]);
|
||||
}
|
||||
|
||||
// Invoke listeners and resolve callables if necessary.
|
||||
foreach ($this->listeners[$event_name] as $priority => &$definitions) {
|
||||
foreach ($definitions as $key => &$definition) {
|
||||
if (!isset($definition['callable'])) {
|
||||
$definition['callable'] = [
|
||||
$this->container->get($definition['service'][0]),
|
||||
$definition['service'][1],
|
||||
];
|
||||
}
|
||||
|
||||
$definition['callable']($event, $event_name, $this);
|
||||
|
||||
$this->addCalledListener($definition, $event_name, $priority);
|
||||
|
||||
if ($event->isPropagationStopped()) {
|
||||
return $event;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($e->isStarted()) {
|
||||
$e->stop();
|
||||
}
|
||||
|
||||
$this->postDispatch($event_name, $event);
|
||||
|
||||
return $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCalledListeners() {
|
||||
return $this->calledListeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getNotCalledListeners() {
|
||||
return $this->notCalledListeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called before dispatching the event.
|
||||
*
|
||||
* @param string $eventName The event name
|
||||
* @param Event $event The event
|
||||
*/
|
||||
protected function preDispatch($eventName, Event $event) {
|
||||
switch ($eventName) {
|
||||
case KernelEvents::VIEW:
|
||||
case KernelEvents::RESPONSE:
|
||||
// stop only if a controller has been executed
|
||||
if ($this->stopwatch->isStarted('controller')) {
|
||||
$this->stopwatch->stop('controller');
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after dispatching the event.
|
||||
*
|
||||
* @param string $eventName The event name
|
||||
* @param Event $event The event
|
||||
*/
|
||||
protected function postDispatch($eventName, Event $event) {
|
||||
switch ($eventName) {
|
||||
case KernelEvents::CONTROLLER:
|
||||
$this->stopwatch->start('controller', 'section');
|
||||
break;
|
||||
case KernelEvents::RESPONSE:
|
||||
$token = $event->getResponse()->headers->get('X-Debug-Token');
|
||||
try {
|
||||
$this->stopwatch->stopSection($token);
|
||||
}
|
||||
catch (\LogicException $e) {
|
||||
}
|
||||
break;
|
||||
case KernelEvents::TERMINATE:
|
||||
// In the special case described in the `preDispatch` method above, the `$token` section
|
||||
// does not exist, then closing it throws an exception which must be caught.
|
||||
$token = $event->getResponse()->headers->get('X-Debug-Token');
|
||||
try {
|
||||
$this->stopwatch->stopSection($token);
|
||||
}
|
||||
catch (\LogicException $e) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $definition
|
||||
* @param $event_name
|
||||
* @param $priority
|
||||
*/
|
||||
private function addCalledListener($definition, $event_name, $priority) {
|
||||
$this->calledListeners[$event_name][$priority][] = [
|
||||
'class' => get_class($definition['callable'][0]),
|
||||
'method' => $definition['callable'][1],
|
||||
];
|
||||
|
||||
// Remove this listener from the $notCalledListeners array.
|
||||
if (!$this->notCalledListeners) {
|
||||
$this->notCalledListeners = $this->cloneListeners($this->listeners);
|
||||
}
|
||||
|
||||
foreach ($this->notCalledListeners[$event_name][$priority] as $key => $listener) {
|
||||
if ($listener['service'][0] == $definition['service'][0] && $listener['service'][1] == $definition['service'][1]) {
|
||||
unset($this->notCalledListeners[$event_name][$priority][$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $listeners
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function cloneListeners($listeners) {
|
||||
$clone = [];
|
||||
|
||||
foreach ($listeners as $eventName => $events) {
|
||||
foreach ($events as $priorityValue => $priorities) {
|
||||
foreach ($priorities as $key => $listener) {
|
||||
$clone[$eventName][$priorityValue][$key]['service'] = [
|
||||
$listener['service'][0],
|
||||
$listener['service'][1]
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace Drupal\webprofiler\Routing;
|
||||
|
||||
use Drupal\Core\ParamConverter\ParamConverterInterface;
|
||||
use Drupal\webprofiler\Profiler\Profiler;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
@@ -11,29 +10,22 @@ use Symfony\Component\Routing\Route;
|
||||
*/
|
||||
class TokenConverter implements ParamConverterInterface {
|
||||
|
||||
/**
|
||||
* @var \Drupal\webprofiler\Profiler\Profiler
|
||||
*/
|
||||
private $profiler;
|
||||
|
||||
/**
|
||||
* Constructs a new WebprofilerController.
|
||||
*
|
||||
* @param \Drupal\webprofiler\Profiler\Profiler $profiler
|
||||
*/
|
||||
public function __construct(Profiler $profiler) {
|
||||
$this->profiler = $profiler;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function convert($value, $definition, $name, array $defaults) {
|
||||
if (NULL === $this->profiler) {
|
||||
// "profiler" service isn't injected to prevent circular reference when
|
||||
// more than one language is active and "Account administration pages" is
|
||||
// enabled on admin/config/regional/language/detection. See #2710787 for
|
||||
// more information.
|
||||
/** @var \Drupal\webprofiler\Profiler\Profiler $profiler */
|
||||
$profiler = \Drupal::service('profiler');
|
||||
|
||||
if (NULL === $profiler) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
$profile = $this->profiler->loadProfile($value);
|
||||
$profile = $profiler->loadProfile($value);
|
||||
|
||||
if (NULL === $profile) {
|
||||
return NULL;
|
||||
|
||||
@@ -2,27 +2,36 @@
|
||||
|
||||
namespace Drupal\webprofiler\State;
|
||||
|
||||
use Drupal\Core\Cache\CacheCollector;
|
||||
use Drupal\Core\State\StateInterface;
|
||||
use Drupal\webprofiler\DataCollector\StateDataCollector;
|
||||
|
||||
/**
|
||||
* Class StateWrapper
|
||||
* Class StateWrapper.
|
||||
*/
|
||||
class StateWrapper implements StateInterface {
|
||||
class StateWrapper extends CacheCollector implements StateInterface {
|
||||
|
||||
/**
|
||||
* The system state.
|
||||
*
|
||||
* @var \Drupal\Core\State\StateInterface
|
||||
*/
|
||||
private $state;
|
||||
|
||||
/**
|
||||
* The state data collector.
|
||||
*
|
||||
* @var \Drupal\webprofiler\DataCollector\StateDataCollector
|
||||
*/
|
||||
private $dataCollector;
|
||||
|
||||
/**
|
||||
* StateWrapper constructor.
|
||||
*
|
||||
* @param \Drupal\Core\State\StateInterface $state
|
||||
* The system state.
|
||||
* @param \Drupal\webprofiler\DataCollector\StateDataCollector $dataCollector
|
||||
* The state data collector.
|
||||
*/
|
||||
public function __construct(StateInterface $state, StateDataCollector $dataCollector) {
|
||||
$this->state = $state;
|
||||
@@ -83,4 +92,34 @@ class StateWrapper implements StateInterface {
|
||||
public function resetCache() {
|
||||
$this->state->resetCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function resolveCacheMiss($key) {
|
||||
return $this->state->resolveCacheMiss($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function destruct() {
|
||||
$this->updateCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Passes through all non-tracked calls onto the decorated object.
|
||||
*
|
||||
* @param string $method
|
||||
* The called method.
|
||||
* @param mixed $args
|
||||
* The passed in arguments.
|
||||
*
|
||||
* @return mixed
|
||||
* The return argument of the call.
|
||||
*/
|
||||
public function __call($method, $args) {
|
||||
return call_user_func_array([$this->state, $method], $args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+42
-1
@@ -19,7 +19,25 @@ class ThemeNegotiatorWrapper extends ThemeNegotiator {
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function determineActiveTheme(RouteMatchInterface $route_match) {
|
||||
foreach ($this->getSortedNegotiators() as $negotiator) {
|
||||
// This method has changed in Drupal 8.4.x, to maintain compatibility with
|
||||
// Drupal 8.3.x we check the existence or not of the classResolver
|
||||
// property.
|
||||
// TODO: remove this logic when we decide to drop Drupal 8.3.x support.
|
||||
if (property_exists($this, 'classResolver')) {
|
||||
$classResolver = $this->classResolver;
|
||||
$negotiators = $this->negotiators;
|
||||
} else {
|
||||
$classResolver = \Drupal::classResolver();
|
||||
$negotiators = $this->getSortedNegotiators();
|
||||
}
|
||||
|
||||
foreach ($negotiators as $negotiator_id) {
|
||||
if (property_exists($this, 'classResolver')) {
|
||||
$negotiator = $classResolver->getInstanceFromDefinition($negotiator_id);
|
||||
} else {
|
||||
$negotiator = $negotiator_id;
|
||||
}
|
||||
|
||||
if ($negotiator->applies($route_match)) {
|
||||
$theme = $negotiator->determineActiveTheme($route_match);
|
||||
if ($theme !== NULL && $this->themeAccess->checkAccess($theme)) {
|
||||
@@ -36,4 +54,27 @@ class ThemeNegotiatorWrapper extends ThemeNegotiator {
|
||||
public function getNegotiator() {
|
||||
return $this->negotiator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sorted array of theme negotiators.
|
||||
*
|
||||
* @return array|\Drupal\Core\Theme\ThemeNegotiatorInterface[]
|
||||
* An array of theme negotiator objects.
|
||||
*
|
||||
* TODO: remove this method when we decide to drop Drupal 8.3.x support.
|
||||
*/
|
||||
protected function getSortedNegotiators() {
|
||||
if (!isset($this->sortedNegotiators)) {
|
||||
// Sort the negotiators according to priority.
|
||||
krsort($this->negotiators);
|
||||
// Merge nested negotiators from $this->negotiators into
|
||||
// $this->sortedNegotiators.
|
||||
$this->sortedNegotiators = [];
|
||||
foreach ($this->negotiators as $builders) {
|
||||
$this->sortedNegotiators = array_merge($this->sortedNegotiators, $builders);
|
||||
}
|
||||
}
|
||||
return $this->sortedNegotiators;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher as BaseTraceableEventDispatcher;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Class TraceableEventDispatcher
|
||||
*/
|
||||
class TraceableEventDispatcher extends BaseTraceableEventDispatcher {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function preDispatch($eventName, Event $event) {
|
||||
switch ($eventName) {
|
||||
case KernelEvents::VIEW:
|
||||
case KernelEvents::RESPONSE:
|
||||
// stop only if a controller has been executed
|
||||
if ($this->stopwatch->isStarted('controller')) {
|
||||
$this->stopwatch->stop('controller');
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function postDispatch($eventName, Event $event) {
|
||||
switch ($eventName) {
|
||||
case KernelEvents::CONTROLLER:
|
||||
$this->stopwatch->start('controller', 'section');
|
||||
break;
|
||||
case KernelEvents::RESPONSE:
|
||||
$token = $event->getResponse()->headers->get('X-Debug-Token');
|
||||
try {
|
||||
$this->stopwatch->stopSection($token);
|
||||
} catch (\LogicException $e) {
|
||||
}
|
||||
break;
|
||||
case KernelEvents::TERMINATE:
|
||||
// In the special case described in the `preDispatch` method above, the `$token` section
|
||||
// does not exist, then closing it throws an exception which must be caught.
|
||||
$token = $event->getResponse()->headers->get('X-Debug-Token');
|
||||
try {
|
||||
$this->stopwatch->stopSection($token);
|
||||
} catch (\LogicException $e) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getListenerPriority($eventName, $listener) {
|
||||
if (!isset($this->listeners[$eventName])) {
|
||||
return;
|
||||
}
|
||||
foreach ($this->listeners[$eventName] as $priority => $listeners) {
|
||||
if (FALSE !== ($key = array_search($listener, $listeners, TRUE))) {
|
||||
return $priority;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+6
-6
@@ -3,6 +3,7 @@
|
||||
namespace Drupal\webprofiler\Views;
|
||||
|
||||
use Drupal\views\ViewExecutable;
|
||||
use Drupal\views\Views;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
@@ -58,6 +59,7 @@ class TraceableViewExecutable extends ViewExecutable {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var \Drupal\views\Plugin\views\exposed_form\ExposedFormPluginInterface $exposed_form */
|
||||
$exposed_form = $this->display_handler->getPlugin('exposed_form');
|
||||
$exposed_form->preRender($this->result);
|
||||
|
||||
@@ -70,11 +72,11 @@ class TraceableViewExecutable extends ViewExecutable {
|
||||
$themes[] = $active_theme->getName();
|
||||
|
||||
// Check for already-cached output.
|
||||
/** @var \Drupal\views\Plugin\views\cache\CachePluginBase $cache */
|
||||
if (!empty($this->live_preview)) {
|
||||
$cache = FALSE;
|
||||
$cache = Views::pluginManager('cache')->createInstance('none');
|
||||
}
|
||||
else {
|
||||
/** @var \Drupal\views\Plugin\views\cache\CachePluginBase $cache */
|
||||
$cache = $this->display_handler->getPlugin('cache');
|
||||
}
|
||||
|
||||
@@ -130,9 +132,7 @@ class TraceableViewExecutable extends ViewExecutable {
|
||||
|
||||
$exposed_form->postRender($this->display_handler->output);
|
||||
|
||||
if ($cache) {
|
||||
$cache->postRender($this->display_handler->output);
|
||||
}
|
||||
$cache->postRender($this->display_handler->output);
|
||||
|
||||
// Let modules modify the view output after it is rendered.
|
||||
$module_handler->invokeAll('views_post_render', [
|
||||
@@ -145,7 +145,7 @@ class TraceableViewExecutable extends ViewExecutable {
|
||||
foreach ($themes as $theme_name) {
|
||||
$function = $theme_name . '_views_post_render';
|
||||
if (function_exists($function)) {
|
||||
$function($this);
|
||||
$function($this, $this->display_handler->output, $cache);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ class WebprofilerServiceProvider extends ServiceProviderBase {
|
||||
$container->addCompilerPass(new StoragePass());
|
||||
|
||||
$container->addCompilerPass(new ServicePass(), PassConfig::TYPE_AFTER_REMOVING);
|
||||
$container->addCompilerPass(new EventPass(), PassConfig::TYPE_AFTER_REMOVING);
|
||||
$container->addCompilerPass(new DecoratorPass(), PassConfig::TYPE_AFTER_REMOVING);
|
||||
|
||||
$modules = $container->getParameter('container.modules');
|
||||
@@ -57,7 +56,7 @@ class WebprofilerServiceProvider extends ServiceProviderBase {
|
||||
'priority' => 78,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
// Add TranslationsDataCollector only if Locale module is enabled.
|
||||
if (isset($modules['locale'])) {
|
||||
$container->register('webprofiler.translations', 'Drupal\webprofiler\DataCollector\TranslationsDataCollector')
|
||||
@@ -105,5 +104,14 @@ class WebprofilerServiceProvider extends ServiceProviderBase {
|
||||
// Replace the regular string_translation service with a traceable one.
|
||||
$container->getDefinition('string_translation')
|
||||
->setClass('Drupal\webprofiler\StringTranslation\TranslationManagerWrapper');
|
||||
|
||||
// Replace the regular event_dispatcher service with a traceable one.
|
||||
$container->getDefinition('event_dispatcher')
|
||||
->setClass('Drupal\webprofiler\EventDispatcher\TraceableEventDispatcher')
|
||||
->addMethodCall('setStopwatch', [new Reference('stopwatch')]);
|
||||
|
||||
$container->getDefinition('http_kernel.basic')
|
||||
->replaceArgument(1, new Reference('webprofiler.debug.controller_resolver'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
{% spaceless %}
|
||||
{% for item in collector.links %}
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<span><a href="{{ item.url }}" title="{{ item.description|default(item.title) }}">{{ item.title }}</a></span>
|
||||
<span>{{ item }}</span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endspaceless %}
|
||||
|
||||
+41
-38
@@ -1,21 +1,22 @@
|
||||
{% block toolbar %}
|
||||
{% set icon %}
|
||||
<a href="{{ url("webprofiler.dashboard", {profile: token}, {fragment: 'events'}) }}" title="{{ 'Events'|t }}">
|
||||
<img width="20" height="28" alt="{{ 'Events'|t }}"
|
||||
src="data:image/png;base64,{{ collector.icon }}">
|
||||
<span class="sf-toolbar-info-piece-additional sf-toolbar-status">{{ collector.getCalledListenersCount }}</span>
|
||||
</a>
|
||||
<a href="{{ url("webprofiler.dashboard", {profile: token}, {fragment: 'events'}) }}" title="{{ 'Events'|t }}">
|
||||
<img width="20" height="28" alt="{{ 'Events'|t }}"
|
||||
src="data:image/png;base64,{{ collector.icon }}">
|
||||
<span class="sf-toolbar-info-piece-additional sf-toolbar-status">{{ collector.getCalledListenersCount }}</span>
|
||||
</a>
|
||||
{% endset %}
|
||||
|
||||
{% set text %}
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>{{ 'Triggered'|t }}</b>
|
||||
<span>{{ collector.getCalledListenersCount }}</span>
|
||||
</div>
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>{{ 'Not triggered'|t }}</b>
|
||||
<span>{{ collector.getNotCalledListenersCount }}</span>
|
||||
</div>
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>{{ 'Called'|t }}</b>
|
||||
<span>{{ collector.getCalledListenersCount }}</span>
|
||||
</div>
|
||||
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>{{ 'Not called'|t }}</b>
|
||||
<span>{{ collector.getNotCalledListenersCount }}</span>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
<div class="sf-toolbar-block">
|
||||
@@ -36,44 +37,46 @@
|
||||
<th>{{ 'Class'|t }}</th>
|
||||
<th>{{ 'Priority'|t }}</th>
|
||||
</thead>
|
||||
<tbody> <% _.each( data.called_listeners, function( item ){ %>
|
||||
<tr>
|
||||
<td><%= item.event %></td>
|
||||
<% if(item.type == 'Method') { %>
|
||||
<td>
|
||||
<%= Drupal.webprofiler.helpers.classLink(item) %>
|
||||
</td>
|
||||
<% } else { %>
|
||||
<td>{{ 'Closure'|t }}</td>
|
||||
<% } %>
|
||||
<td><%= item.priority %></td>
|
||||
</tr>
|
||||
<tbody>
|
||||
<% _.each( data.called_listeners, function( events, event_name ){ %>
|
||||
<% _.each( events, function( priority, priority_value ){ %>
|
||||
<% _.each( priority, function( listener ){ %>
|
||||
<tr>
|
||||
<td><%= event_name %></td>
|
||||
<% if( listener.clazz ) { %>
|
||||
<td><%= Drupal.webprofiler.helpers.classLink(listener.clazz) %></td>
|
||||
<% } else { %>
|
||||
<td><%= listener.service[0] %>::<%= listener.service[1] %></td>
|
||||
<% } %>
|
||||
<td><%= priority_value %></td>
|
||||
</tr>
|
||||
<% }); %>
|
||||
<% }); %>
|
||||
<% }); %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="panel__container">
|
||||
|
||||
<table class="table--duo">
|
||||
<thead>
|
||||
<th>{{ 'Non called listeners'|t }}</th>
|
||||
<th>{{ 'Class'|t }}</th>
|
||||
<th>{{ 'Not called listeners'|t }}</th>
|
||||
<th>{{ 'Service'|t }}</th>
|
||||
<th>{{ 'Priority'|t }}</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% _.each( data.not_called_listeners, function( item ){ %>
|
||||
<% _.each( data.not_called_listeners, function( events, event_name ){ %>
|
||||
<% _.each( events, function( priority, priority_value ){ %>
|
||||
<% _.each( priority, function( listener ){ %>
|
||||
<tr>
|
||||
<td><%= item.event %></td>
|
||||
<% if(item.type == 'Method') { %>
|
||||
<td>
|
||||
<%= Drupal.webprofiler.helpers.classLink(item) %>
|
||||
</td>
|
||||
<% } else { %>
|
||||
<td>{{ 'Closure'|t }}</td>
|
||||
<% } %>
|
||||
<td><%= event_name %></td>
|
||||
<td><%= listener.service[0] %>::<%= listener.service[1] %></td>
|
||||
<td><%= priority_value %></td>
|
||||
</tr>
|
||||
<% }); %>
|
||||
<% }); %>
|
||||
<% }); %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\webprofiler\FunctionalJavascript;
|
||||
|
||||
/**
|
||||
* Tests the JavaScript functionality of webprofiler.
|
||||
*
|
||||
* @group webprofiler
|
||||
*/
|
||||
class ToolbarTest extends WebprofilerTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['webprofiler', 'node'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
\Drupal::configFactory()->getEditable('system.site')->set('page.front', '/node')->save(TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the toolbar appears on front page.
|
||||
*/
|
||||
public function testToolbarOnFrontPage() {
|
||||
$this->loginForToolbar();
|
||||
|
||||
$this->drupalGet('<front>');
|
||||
|
||||
$this->waitForToolbar();
|
||||
|
||||
$assert = $this->assertSession();
|
||||
$assert->pageTextContains(\Drupal::VERSION);
|
||||
$assert->pageTextContains('Configure Webprofiler');
|
||||
$assert->pageTextContains('View latest reports');
|
||||
$assert->pageTextContains('Drupal Documentation');
|
||||
$assert->pageTextContains('Get involved!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the toolbar report page.
|
||||
*/
|
||||
public function testToolbarReportPage() {
|
||||
$this->loginForDashboard();
|
||||
|
||||
$this->drupalGet('<front>');
|
||||
|
||||
$token = $this->waitForToolbar();
|
||||
|
||||
$this->drupalGet('admin/reports/profiler/list');
|
||||
|
||||
$assert = $this->assertSession();
|
||||
$assert->pageTextContains($token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the toolbar not appears on excluded path.
|
||||
*/
|
||||
public function testToolbarNotAppearsOnExcludedPath() {
|
||||
$this->loginForDashboard();
|
||||
|
||||
$this->drupalGet('admin/config/development/devel');
|
||||
$token = $this->waitForToolbar();
|
||||
$assert = $this->assertSession();
|
||||
$assert->pageTextContains($token);
|
||||
$assert->pageTextContains('Configure Webprofiler');
|
||||
|
||||
$this->config('webprofiler.config')
|
||||
->set('exclude', '/admin/config/development/devel')
|
||||
->save();
|
||||
$this->drupalGet('admin/config/development/devel');
|
||||
$this->assertSession()->pageTextNotContains('sf-toolbar');
|
||||
}
|
||||
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\webprofiler\FunctionalJavascript;
|
||||
|
||||
use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
|
||||
use PHPUnit_Framework_AssertionFailedError;
|
||||
|
||||
/**
|
||||
* Class WebprofilerTestBase.
|
||||
*
|
||||
* @group webprofiler
|
||||
*/
|
||||
abstract class WebprofilerTestBase extends JavascriptTestBase {
|
||||
|
||||
/**
|
||||
* Wait until the toolbar is present on page.
|
||||
*/
|
||||
protected function waitForToolbar() {
|
||||
$session = $this->getSession();
|
||||
$token = $this->getToken();
|
||||
$page = $session->getPage();
|
||||
|
||||
$toolbar = $page->findById('webprofiler' . $token);
|
||||
$this->assertTrue($toolbar->hasClass('sf-toolbar'), 'Toolbar loader is present in page');
|
||||
|
||||
$session->wait(1000, 'null !== document.getElementById(\'sfToolbarMainContent-' . $token . '\')');
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Webprofiler token.
|
||||
*
|
||||
* @return null|string
|
||||
* The page token
|
||||
*/
|
||||
protected function getToken() {
|
||||
$token = $this->getSession()->getResponseHeader('X-Debug-Token');
|
||||
|
||||
if (NULL === $token) {
|
||||
throw new PHPUnit_Framework_AssertionFailedError();
|
||||
}
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Login with a user that can see the toolbar.
|
||||
*/
|
||||
protected function loginForToolbar() {
|
||||
$admin_user = $this->drupalCreateUser(
|
||||
[
|
||||
'view webprofiler toolbar',
|
||||
]
|
||||
);
|
||||
$this->drupalLogin($admin_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Login with a user that can see the toolbar and the dashboard.
|
||||
*/
|
||||
protected function loginForDashboard() {
|
||||
$admin_user = $this->drupalCreateUser(
|
||||
[
|
||||
'view webprofiler toolbar',
|
||||
'access webprofiler',
|
||||
]
|
||||
);
|
||||
$this->drupalLogin($admin_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush cache.
|
||||
*/
|
||||
protected function flushCache() {
|
||||
$module_handler = \Drupal::moduleHandler();
|
||||
$module_handler->invokeAll('cache_flush');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\webprofiler\Kernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Class DecoratorTest.
|
||||
*
|
||||
* @group webprofiler
|
||||
*/
|
||||
class DecoratorTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['system', 'views'];
|
||||
|
||||
/**
|
||||
* Tests the Entity Type Manager service decoration.
|
||||
*
|
||||
* @param string $service
|
||||
* The service name.
|
||||
* @param string $original
|
||||
* The original class.
|
||||
* @param string $decorated
|
||||
* The decorated class.
|
||||
*
|
||||
* @dataProvider decorators
|
||||
*/
|
||||
public function testEntityTypeDecorator($service, $original, $decorated) {
|
||||
$entityTypeManagerOriginal = $this->container->get($service);
|
||||
|
||||
$this->assertInstanceOf($original, $entityTypeManagerOriginal);
|
||||
|
||||
$this->container->get('module_installer')->install(['webprofiler']);
|
||||
|
||||
$entityTypeManagerDecorated = $this->container->get($service);
|
||||
|
||||
$this->assertInstanceOf($decorated, $entityTypeManagerDecorated);
|
||||
}
|
||||
|
||||
/**
|
||||
* DataProvider for testEntityTypeDecorator.
|
||||
*
|
||||
* @return array
|
||||
* The array of values to run tests on.
|
||||
*/
|
||||
public function decorators() {
|
||||
return array(
|
||||
array('entity_type.manager', 'Drupal\Core\Entity\EntityTypeManager', 'Drupal\webprofiler\Entity\EntityManagerWrapper'),
|
||||
array('cache_factory', 'Drupal\Core\Cache\MemoryBackendFactory', 'Drupal\webprofiler\Cache\CacheFactoryWrapper'),
|
||||
array('asset.css.collection_renderer', 'Drupal\Core\Asset\CssCollectionRenderer', 'Drupal\webprofiler\Asset\CssCollectionRendererWrapper'),
|
||||
array('asset.js.collection_renderer', 'Drupal\Core\Asset\JsCollectionRenderer', 'Drupal\webprofiler\Asset\JsCollectionRendererWrapper'),
|
||||
array('state', 'Drupal\Core\State\State', 'Drupal\webprofiler\State\StateWrapper'),
|
||||
array('views.executable', 'Drupal\views\ViewExecutableFactory', 'Drupal\webprofiler\Views\ViewExecutableFactoryWrapper'),
|
||||
array('form_builder', 'Drupal\Core\Form\FormBuilder', 'Drupal\webprofiler\Form\FormBuilderWrapper'),
|
||||
array('access_manager', 'Drupal\Core\Access\AccessManager', 'Drupal\webprofiler\Access\AccessManagerWrapper'),
|
||||
array('theme.negotiator', 'Drupal\Core\Theme\ThemeNegotiator', 'Drupal\webprofiler\Theme\ThemeNegotiatorWrapper'),
|
||||
array('config.factory', 'Drupal\Core\Config\ConfigFactory', 'Drupal\webprofiler\Config\ConfigFactoryWrapper'),
|
||||
array('string_translation', 'Drupal\Core\StringTranslation\TranslationManager', 'Drupal\webprofiler\StringTranslation\TranslationManagerWrapper'),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\webprofiler\Unit\DataCollector;
|
||||
|
||||
use Drupal\webprofiler\Asset\CssCollectionRendererWrapper;
|
||||
use Drupal\webprofiler\DataCollector\AssetsDataCollector;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\webprofiler\DataCollector\AssetsDataCollector
|
||||
*
|
||||
* @group webprofiler
|
||||
*/
|
||||
class AssetsDataCollectorTest extends DataCollectorBaseTest {
|
||||
|
||||
/**
|
||||
* @var \Drupal\webprofiler\DataCollector\AssetsDataCollector
|
||||
*/
|
||||
private $assetDataCollector;
|
||||
|
||||
/**
|
||||
* @var \PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
private $assetCollectionRendererInterface;
|
||||
|
||||
const ROOT = 'test_root';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->assetDataCollector = new AssetsDataCollector(AssetsDataCollectorTest::ROOT);
|
||||
$this->assetCollectionRendererInterface = $this->getMock('Drupal\Core\Asset\AssetCollectionRendererInterface');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the Assets data collector.
|
||||
*/
|
||||
public function testCSS() {
|
||||
$css = [
|
||||
'core/assets/vendor/normalize-css/normalize.css' => [
|
||||
'weight' => -219.944,
|
||||
'group' => 0,
|
||||
'type' => 'file',
|
||||
'data' => 'core\/assets\/vendor\/normalize-css\/normalize.css',
|
||||
'version' => '3.0.3',
|
||||
'media' => 'all',
|
||||
'preprocess' => TRUE,
|
||||
'browsers' => [
|
||||
'IE' => TRUE,
|
||||
'!IE' => TRUE,
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$cssCollectionRendererWrapper = new CssCollectionRendererWrapper($this->assetCollectionRendererInterface, $this->assetDataCollector);
|
||||
$cssCollectionRendererWrapper->render($css);
|
||||
|
||||
$this->assertEquals(1, $this->assetDataCollector->getCssCount());
|
||||
|
||||
$this->assetDataCollector->collect($this->request, $this->response, $this->exception);
|
||||
|
||||
$data = $this->assetDataCollector->getData();
|
||||
$this->assertEquals(AssetsDataCollectorTest::ROOT . '/', $data['assets']['installation_path']);
|
||||
}
|
||||
|
||||
}
|
||||
+4
-4
@@ -1,16 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\webprofiler\Unit\Cache;
|
||||
namespace Drupal\Tests\webprofiler\Unit\DataCollector;
|
||||
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Drupal\webprofiler\Cache\CacheBackendWrapper;
|
||||
use Drupal\webprofiler\DataCollector\CacheDataCollector;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\webprofiler\DataCollector\CacheDataCollector
|
||||
*
|
||||
* @group webprofiler
|
||||
*/
|
||||
class CacheDataCollectorTest extends UnitTestCase {
|
||||
class CacheDataCollectorTest extends DataCollectorBaseTest {
|
||||
|
||||
/**
|
||||
* @var \Drupal\webprofiler\DataCollector\CacheDataCollector
|
||||
@@ -50,7 +50,7 @@ class CacheDataCollectorTest extends UnitTestCase {
|
||||
* Tests the collection of a cache hit.
|
||||
*/
|
||||
public function testCacheCollectorHit() {
|
||||
$cache = new \StdClass();
|
||||
$cache = new \stdClass();
|
||||
$cache->cid = 'cache_id';
|
||||
$cache->expire = 1;
|
||||
$cache->tags = ['tag1', 'tag2'];
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\webprofiler\Unit\DataCollector;
|
||||
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* Class DataCollectorBaseTest.
|
||||
*
|
||||
* @group webprofiler
|
||||
*/
|
||||
abstract class DataCollectorBaseTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
protected $response;
|
||||
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
protected $exception;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->request = $this->getMock('Symfony\Component\HttpFoundation\Request');
|
||||
$this->response = $this->getMock('Symfony\Component\HttpFoundation\Response');
|
||||
$this->exception = $this->getMock('Exception');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,8 +9,8 @@ tags:
|
||||
dependencies:
|
||||
- devel
|
||||
|
||||
# Information added by Drupal.org packaging script on 2016-09-03
|
||||
version: '8.x-1.0-alpha1+23-dev'
|
||||
# Information added by Drupal.org packaging script on 2017-08-14
|
||||
version: '8.x-1.0'
|
||||
core: '8.x'
|
||||
project: 'devel'
|
||||
datestamp: 1472897643
|
||||
datestamp: 1502732047
|
||||
|
||||
@@ -97,10 +97,9 @@ function webprofiler_requirements($phase) {
|
||||
|
||||
if (!$has_d3) {
|
||||
$requirements['d3js']['severity'] = REQUIREMENT_WARNING;
|
||||
$d3_url = Url::fromUri('https://github.com/mbostock/d3', ['attributes' => ['target' => '_blank']]);
|
||||
$requirements['d3js']['description'] = [
|
||||
'#prefix' => ' ',
|
||||
'#markup' => t('Webprofiler module requires D3.js library to properly render data. <a href=":d3_link">Download D3.js</a> into /libraries/d3/d3.min.js.', [':d3_link' => $d3_url->toUriString()])
|
||||
'#markup' => t('Webprofiler module requires D3.js library to properly render data. Composer based install recommended, see README.md file for instructions.'),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -112,10 +111,9 @@ function webprofiler_requirements($phase) {
|
||||
|
||||
if (!$has_highlight) {
|
||||
$requirements['highlightjs']['severity'] = REQUIREMENT_WARNING;
|
||||
$highlight_url = Url::fromUri('http://highlightjs.org', ['attributes' => ['target' => '_blank']]);
|
||||
$requirements['highlightjs']['description'] = [
|
||||
'#prefix' => ' ',
|
||||
'#markup' => t('Webprofiler module requires highlight.js library to syntax highlight collected queries. <a href=":highlight_link">Download highlight.js</a> assets into /libraries/highlight.', [':highlight_link' => $highlight_url->toUriString()])
|
||||
'#markup' => t('Webprofiler module requires highlight.js library to syntax highlight collected queries. Composer based install recommended, see README.md file for instructions.'),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
d3:
|
||||
remote: https://github.com/mbostock/d3
|
||||
version: 3.4.4
|
||||
remote: https://github.com/d3/d3
|
||||
version: 3.5.17
|
||||
license:
|
||||
name: BSD-3
|
||||
url: https://github.com/mbostock/d3/blob/master/LICENSE
|
||||
url: https://github.com/d3/d3/blob/master/LICENSE
|
||||
gpl-compatible: true
|
||||
js:
|
||||
libraries/d3/d3.min.js: {}
|
||||
highlight:
|
||||
/libraries/d3/d3.min.js: {}
|
||||
highlightjs:
|
||||
remote: https://github.com/isagalaev/highlight.js
|
||||
version: 8.0
|
||||
version: 9.7.0
|
||||
license:
|
||||
name: BSD-3
|
||||
url: https://github.com/isagalaev/highlight.js/blob/master/LICENSE
|
||||
gpl-compatible: true
|
||||
js:
|
||||
libraries/highlightjs/highlight.pack.js: {}
|
||||
/libraries/highlightjs/highlight.pack.js: {}
|
||||
css:
|
||||
component:
|
||||
libraries/highlightjs/styles/idea.css: {}
|
||||
/libraries/highlightjs/styles/idea.css: {}
|
||||
dashboard:
|
||||
version: 2.0
|
||||
js:
|
||||
@@ -48,7 +48,7 @@ database:
|
||||
- core/jquery
|
||||
- core/drupal
|
||||
- core/drupalSettings
|
||||
- webprofiler/highlight
|
||||
- webprofiler/highlightjs
|
||||
timeline:
|
||||
version: 2.0
|
||||
js:
|
||||
|
||||
@@ -2,11 +2,11 @@ core: "8.x"
|
||||
api: 2
|
||||
|
||||
libraries:
|
||||
highlight:
|
||||
highlightjs:
|
||||
download:
|
||||
type: "git"
|
||||
url: "git@github.com:mbostock/d3.git"
|
||||
url: "https://github.com/isagalaev/highlight.js.git"
|
||||
d3:
|
||||
download:
|
||||
type: "git"
|
||||
url: "git@github.com:mbostock/d3.git"
|
||||
url: "https://github.com/d3/d3.git"
|
||||
|
||||
@@ -22,7 +22,7 @@ webprofiler.frontend.save:
|
||||
type: 'webprofiler:token'
|
||||
methods: [POST]
|
||||
requirements:
|
||||
_permission: 'access webprofiler'
|
||||
_permission: 'view webprofiler toolbar'
|
||||
|
||||
# view profile
|
||||
webprofiler.dashboard:
|
||||
|
||||
@@ -47,7 +47,7 @@ services:
|
||||
# event subscribers
|
||||
webprofiler.profiler_listener:
|
||||
class: Symfony\Component\HttpKernel\EventListener\ProfilerListener
|
||||
arguments: ['@profiler', '@?webprofiler.matcher', '%webprofiler.only_exceptions%', '%webprofiler.only_master_requests%', '@request_stack']
|
||||
arguments: ['@profiler', '@request_stack', '@?webprofiler.matcher', '%webprofiler.only_exceptions%', '%webprofiler.only_master_requests%']
|
||||
tags:
|
||||
- { name: event_subscriber }
|
||||
|
||||
@@ -187,13 +187,9 @@ services:
|
||||
stopwatch:
|
||||
class: Drupal\webprofiler\Stopwatch
|
||||
|
||||
webprofiler.debug.event_dispatcher.default:
|
||||
class: Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher
|
||||
arguments: ['@service_container']
|
||||
|
||||
webprofiler.debug.plugin.manager.mail.default:
|
||||
class: Drupal\Core\Mail\MailManager
|
||||
arguments: ['@container.namespaces', '@cache.discovery', '@module_handler', '@config.factory', '@logger.factory', '@string_translation']
|
||||
arguments: ['@container.namespaces', '@cache.discovery', '@module_handler', '@config.factory', '@logger.factory', '@string_translation', '@renderer']
|
||||
|
||||
webprofiler.debug.controller_resolver:
|
||||
class: Symfony\Component\HttpKernel\Controller\TraceableControllerResolver
|
||||
@@ -253,7 +249,6 @@ services:
|
||||
# parameter converter service for profile token
|
||||
webprofiler.token_converter:
|
||||
class: Drupal\webprofiler\Routing\TokenConverter
|
||||
arguments: ['@profiler']
|
||||
tags:
|
||||
- { name: paramconverter }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user