a few more base modules
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
!! README.md is a work in progress !!
|
||||
|
||||
#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:
|
||||
|
||||
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:
|
||||
|
||||
- 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
|
||||
|
||||
#Timeline:
|
||||
|
||||
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):
|
||||
|
||||
```
|
||||
$class_loader->addPsr4('Drupal\\webprofiler\\', [ __DIR__ . '/../../modules/contrib/devel/webprofiler/src']);
|
||||
$settings['container_base_class'] = '\Drupal\webprofiler\DependencyInjection\TraceableContainer';
|
||||
```
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"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"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
purge_on_cache_clear: true
|
||||
storage: profiler.database_storage
|
||||
exclude: "/contextual/*\r\n/toolbar/*\r\n/edit/*\r\n*.js\r\n*.css"
|
||||
ide_link: "txmt://open?url=file://@file&line=@line"
|
||||
ide_link_remote: ""
|
||||
ide_link_local: ""
|
||||
active_toolbar_items:
|
||||
devel: devel
|
||||
assets: assets
|
||||
blocks: blocks
|
||||
cache: cache
|
||||
database: database
|
||||
drupal_extension: drupal_extension
|
||||
forms: forms
|
||||
performance_timing: performance_timing
|
||||
php_config: php_config
|
||||
request: request
|
||||
time: time
|
||||
user: user
|
||||
views: views
|
||||
config: '0'
|
||||
events: '0'
|
||||
http: '0'
|
||||
routing: '0'
|
||||
services: '0'
|
||||
state: '0'
|
||||
query_sort: source
|
||||
query_highlight: 5
|
||||
@@ -0,0 +1,35 @@
|
||||
# Schema for the configuration files of the Webprofiler module.
|
||||
webprofiler.config:
|
||||
type: config_object
|
||||
label: 'Webprofiler configuration'
|
||||
mapping:
|
||||
purge_on_cache_clear:
|
||||
type: boolean
|
||||
label: 'Purge profiles on cache clear'
|
||||
storage:
|
||||
type: string
|
||||
label: 'Storage implementation'
|
||||
exclude:
|
||||
type: string
|
||||
label: 'Paths to exclude'
|
||||
active_toolbar_items:
|
||||
type: sequence
|
||||
label: 'Active toolbar items'
|
||||
sequence:
|
||||
- type: string
|
||||
label: 'Toolbar item'
|
||||
ide_link:
|
||||
type: string
|
||||
label: 'IDE link'
|
||||
ide_link_remote:
|
||||
type: string
|
||||
label: 'IDE link remote path'
|
||||
ide_link_local:
|
||||
type: string
|
||||
label: 'IDE link local path'
|
||||
query_sort:
|
||||
type: string
|
||||
label: 'Sort query log'
|
||||
query_highlight:
|
||||
type: integer
|
||||
label: 'Slow query highlighting'
|
||||
@@ -0,0 +1,54 @@
|
||||
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.
|
||||
@@ -0,0 +1,54 @@
|
||||
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,582 @@
|
||||
/* base themes */
|
||||
#webrofiler {
|
||||
overflow: hidden;
|
||||
font-size: 1em;
|
||||
line-height: 1.2;
|
||||
color: rgba(0, 0, 0, 0.7);
|
||||
background-color: #ffffff; }
|
||||
#webrofiler * {
|
||||
box-sizing: border-box; }
|
||||
|
||||
#webprofiler {
|
||||
/*
|
||||
|
||||
overview example
|
||||
|
||||
<ul class="overview__list">
|
||||
<li>
|
||||
<a class="overview__link">
|
||||
<img class="overview__icon" src="data:">
|
||||
<span class="overview__title">
|
||||
<span class="overview__subtitle">
|
||||
|
||||
|
||||
*/
|
||||
/*
|
||||
|
||||
general element template
|
||||
|
||||
will be implemented in the next dashboard relase
|
||||
|
||||
this HTML will contain the preview of any single card present inside the dashboard
|
||||
|
||||
<a class="ui--[red||pink||purple||deepPurple||indigo||blue||lightBlue||cyan||teal||green||lightGreen||lime||yellow||amber||orange||deepOrange||brown||gray||blueGray] app l-card">
|
||||
|
||||
<div class="app__bar">
|
||||
<h3 class="app__title">
|
||||
<img class="app__icon" src="data:">
|
||||
|
||||
<div class="app__container">
|
||||
<div class="app__main-data"> text
|
||||
<div class="app__secondary-data"> text
|
||||
|
||||
*/ }
|
||||
#webprofiler .tabs:after, #webprofiler .tabs__tabs:after, #webprofiler .tabs__panels:after, #webprofiler .tabs__panel:after, #webprofiler .resume:after, #webprofiler .collectors:after, #webprofiler .panel:after, #webprofiler .panel__toolbar:after, #webprofiler .panel__container:after, #webprofiler .panel__expand-header:after, #webprofiler .panel__expand-content:after, #webprofiler .app__bar:after, #webprofiler .app__container:after, #webprofiler .modal__bar:after, #webprofiler .modal__content:after {
|
||||
content: ' ';
|
||||
display: block;
|
||||
clear: both; }
|
||||
#webprofiler .panel__container, #webprofiler .ui--red, #webprofiler .ui--pink, #webprofiler .ui--purple, #webprofiler .ui--deepPurple, #webprofiler .ui--indigo, #webprofiler .ui--blue, #webprofiler .ui--lightBlue, #webprofiler .ui--cyan, #webprofiler .ui--teal, #webprofiler .ui--green, #webprofiler .ui--lightGreen, #webprofiler .ui--lime, #webprofiler .ui--yellow, #webprofiler .ui--amber, #webprofiler .ui--orange, #webprofiler .ui--deepOrange, #webprofiler .ui--brown, #webprofiler .ui--gray, #webprofiler .ui--blueGray, #webprofiler .modal__container {
|
||||
box-shadow: 0 2px 4px -2px #000000;
|
||||
margin-bottom: 40px;
|
||||
border-bottom-right-radius: 2px;
|
||||
border-bottom-left-radius: 2px;
|
||||
overflow: hidden; }
|
||||
#webprofiler .ui--red .app__title, #webprofiler .ui--pink .app__title, #webprofiler .ui--purple .app__title, #webprofiler .ui--deepPurple .app__title, #webprofiler .ui--blue .app__title, #webprofiler .ui--lightBlue .app__title, #webprofiler .ui--cyan .app__title, #webprofiler .ui--green .app__title, #webprofiler .ui--lightGreen .app__title, #webprofiler .ui--lime .app__title, #webprofiler .ui--yellow .app__title, #webprofiler .ui--amber .app__title, #webprofiler .ui--orange .app__title, #webprofiler .ui--deepOrange .app__title, #webprofiler .ui--brown .app__title {
|
||||
color: white; }
|
||||
#webprofiler .ui--indigo .app__title, #webprofiler .ui--teal .app__title, #webprofiler .ui--gray .app__title, #webprofiler .ui--blueGray .app__title, #webprofiler .modal__title {
|
||||
color: rgba(0, 0, 0, 0.87); }
|
||||
#webprofiler .ui--red .app__main-data, #webprofiler .ui--pink .app__main-data, #webprofiler .ui--purple .app__main-data, #webprofiler .ui--deepPurple .app__main-data, #webprofiler .ui--indigo .app__main-data, #webprofiler .ui--blue .app__main-data, #webprofiler .ui--lightBlue .app__main-data, #webprofiler .ui--cyan .app__main-data, #webprofiler .ui--teal .app__main-data, #webprofiler .ui--green .app__main-data, #webprofiler .ui--lightGreen .app__main-data, #webprofiler .ui--lime .app__main-data, #webprofiler .ui--yellow .app__main-data, #webprofiler .ui--amber .app__main-data, #webprofiler .ui--orange .app__main-data, #webprofiler .ui--deepOrange .app__main-data, #webprofiler .ui--brown .app__main-data, #webprofiler .ui--gray .app__main-data, #webprofiler .ui--blueGray .app__main-data, #webprofiler .modal__content {
|
||||
color: rgba(0, 0, 0, 0.7); }
|
||||
#webprofiler .ui--red .app__secondary-data, #webprofiler .ui--pink .app__secondary-data, #webprofiler .ui--purple .app__secondary-data, #webprofiler .ui--deepPurple .app__secondary-data, #webprofiler .ui--indigo .app__secondary-data, #webprofiler .ui--blue .app__secondary-data, #webprofiler .ui--lightBlue .app__secondary-data, #webprofiler .ui--cyan .app__secondary-data, #webprofiler .ui--teal .app__secondary-data, #webprofiler .ui--green .app__secondary-data, #webprofiler .ui--lightGreen .app__secondary-data, #webprofiler .ui--lime .app__secondary-data, #webprofiler .ui--yellow .app__secondary-data, #webprofiler .ui--amber .app__secondary-data, #webprofiler .ui--orange .app__secondary-data, #webprofiler .ui--deepOrange .app__secondary-data, #webprofiler .ui--brown .app__secondary-data, #webprofiler .ui--gray .app__secondary-data, #webprofiler .ui--blueGray .app__secondary-data {
|
||||
color: rgba(0, 0, 0, 0.3); }
|
||||
#webprofiler a {
|
||||
color: #2979ff; }
|
||||
#webprofiler a:hover {
|
||||
color: #2962ff;
|
||||
text-decoration: none; }
|
||||
#webprofiler .list--unstyled {
|
||||
margin: 0;
|
||||
list-style-type: none;
|
||||
padding: 0; }
|
||||
#webprofiler .list--unstyled > li {
|
||||
display: block;
|
||||
padding: 0 8px; }
|
||||
#webprofiler .list--inline {
|
||||
margin: 0;
|
||||
list-style-type: none;
|
||||
padding: 0; }
|
||||
#webprofiler .list--inline > li {
|
||||
display: inline-block;
|
||||
padding: 8px; }
|
||||
#webprofiler .list--level-0 li {
|
||||
padding: 0 0 0 0; }
|
||||
#webprofiler .list--level-1 li {
|
||||
padding: 0 0 0 8px; }
|
||||
#webprofiler .list--level-3 li {
|
||||
padding: 0 0 0 16px; }
|
||||
#webprofiler pre {
|
||||
padding: 16px; }
|
||||
#webprofiler pre, #webprofiler code {
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
word-break: break-word;
|
||||
font-size: 14px;
|
||||
background-color: #f5f5f5; }
|
||||
#webprofiler .list-item--bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
#webprofiler code.code--json {
|
||||
white-space: pre-wrap; }
|
||||
#webprofiler thead {
|
||||
white-space: nowrap; }
|
||||
#webprofiler thead th {
|
||||
background-color: transparent; }
|
||||
#webprofiler th {
|
||||
font-size: 12px;
|
||||
color: rgba(0, 0, 0, 0.54);
|
||||
border: none; }
|
||||
#webprofiler td {
|
||||
word-break: break-all;
|
||||
font-size: 13px;
|
||||
color: rgba(0, 0, 0, 0.87); }
|
||||
#webprofiler tr {
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.18); }
|
||||
#webprofiler tr:hover {
|
||||
background-color: #eeeeee; }
|
||||
#webprofiler td, #webprofiler th {
|
||||
padding: 16px 24px;
|
||||
min-height: 46px;
|
||||
text-transform: none;
|
||||
vertical-align: top; }
|
||||
#webprofiler .table--compact {
|
||||
table-layout: fixed; }
|
||||
#webprofiler .table--compact td, #webprofiler .table--compact th {
|
||||
word-spacing: 99em; }
|
||||
#webprofiler .table--fixed {
|
||||
table-layout: fixed; }
|
||||
#webprofiler .table--duo th:first-child,
|
||||
#webprofiler .table--duo td:first-child {
|
||||
width: 35%; }
|
||||
#webprofiler .table--trio th:first-child,
|
||||
#webprofiler .table--trio td:first-child {
|
||||
width: 80%; }
|
||||
#webprofiler .table--trio th:nth-child(2),
|
||||
#webprofiler .table--trio td:nth-child(2),
|
||||
#webprofiler .table--trio th:nth-child(3),
|
||||
#webprofiler .table--trio td:nth-child(3) {
|
||||
width: 10%; }
|
||||
#webprofiler [class*="button"] {
|
||||
display: block;
|
||||
color: #2979ff;
|
||||
border-bottom-right-radius: 2px;
|
||||
border-bottom-left-radius: 2px;
|
||||
border: none;
|
||||
text-transform: uppercase;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
padding: 16px;
|
||||
margin: 8px;
|
||||
cursor: pointer;
|
||||
min-width: 42px; }
|
||||
#webprofiler .button--flat {
|
||||
background-color: transparent; }
|
||||
#webprofiler .button--flat:hover {
|
||||
background-color: #eeeeee; }
|
||||
#webprofiler .button--flat:active, #webprofiler .button--flat:focus {
|
||||
background-color: #bdbdbd; }
|
||||
#webprofiler .tabs {
|
||||
position: relative;
|
||||
margin: -16px 0 16px 0; }
|
||||
#webprofiler .tabs a {
|
||||
display: inline; }
|
||||
#webprofiler .tabs__radio {
|
||||
display: none; }
|
||||
#webprofiler .tabs__tabs {
|
||||
margin: 0 -16px 16px -16px;
|
||||
text-transform: uppercase;
|
||||
height: 48px;
|
||||
background-color: #f5f5f5; }
|
||||
#webprofiler .tabs__tabs > li {
|
||||
padding: 0; }
|
||||
#webprofiler .tabs__label {
|
||||
border-bottom: 5px solid transparent;
|
||||
text-align: center;
|
||||
line-height: 48px;
|
||||
min-width: 160px;
|
||||
display: inline-block;
|
||||
margin: -5px 0 0 0; }
|
||||
#webprofiler .tabs__panel {
|
||||
display: none;
|
||||
width: 100%; }
|
||||
#webprofiler .tabs__radio:checked:nth-child(1) ~ .tabs__panels .tabs__panel:nth-child(1) {
|
||||
display: block; }
|
||||
#webprofiler .tabs__radio:checked:nth-child(2) ~ .tabs__panels .tabs__panel:nth-child(2) {
|
||||
display: block; }
|
||||
#webprofiler .tabs__radio:checked:nth-child(3) ~ .tabs__panels .tabs__panel:nth-child(3) {
|
||||
display: block; }
|
||||
#webprofiler .tabs__radio:checked:nth-child(1) ~ .tabs__tabs li:nth-child(1) .tabs__label {
|
||||
border-bottom-color: #2962ff; }
|
||||
#webprofiler .tabs__radio:checked:nth-child(2) ~ .tabs__tabs li:nth-child(2) .tabs__label {
|
||||
border-bottom-color: #2962ff; }
|
||||
#webprofiler .tabs__radio:checked:nth-child(3) ~ .tabs__tabs li:nth-child(3) .tabs__label {
|
||||
border-bottom-color: #2962ff; }
|
||||
#webprofiler .overview {
|
||||
display: table-cell;
|
||||
width: 20%;
|
||||
vertical-align: top;
|
||||
font-size: 1.2em;
|
||||
line-height: 2em;
|
||||
background: #ffffff; }
|
||||
#webprofiler .overview__list {
|
||||
padding: 0;
|
||||
margin: -1px -1px 0 0;
|
||||
list-style-type: none;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.18); }
|
||||
#webprofiler .overview__link {
|
||||
position: relative;
|
||||
display: block;
|
||||
padding: 0 0 0 72px;
|
||||
border-right: 5px solid transparent; }
|
||||
#webprofiler .overview__link:after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: -5px;
|
||||
left: 72px;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.18); }
|
||||
#webprofiler .overview__icon {
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
left: 16px; }
|
||||
#webprofiler .overview__title {
|
||||
display: block;
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
padding-top: 16px;
|
||||
padding-bottom: 16px;
|
||||
padding-right: 11px; }
|
||||
#webprofiler .overview__subtitle {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
padding-bottom: 16px;
|
||||
padding-right: 11px;
|
||||
margin-top: -16px; }
|
||||
#webprofiler .is--selected .overview__link {
|
||||
color: #000000;
|
||||
background-color: #f5f5f5;
|
||||
border-right-color: #2962ff; }
|
||||
#webprofiler .overview__link:hover {
|
||||
color: #000000;
|
||||
background-color: #82b1ff; }
|
||||
#webprofiler .resume {
|
||||
position: relative;
|
||||
background-color: #e0e0e0;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.18);
|
||||
border-left: 1px solid rgba(0, 0, 0, 0.18);
|
||||
padding: 16px 16px 0 16px;
|
||||
margin-left: 20%; }
|
||||
#webprofiler .resume__title {
|
||||
display: block;
|
||||
font-size: 24px;
|
||||
line-height: 32px; }
|
||||
#webprofiler .resume__subtitle,
|
||||
#webprofiler .resume__time {
|
||||
display: block;
|
||||
font-size: 16px;
|
||||
line-height: 24px; }
|
||||
#webprofiler .resume__time {
|
||||
margin-bottom: 52px; }
|
||||
#webprofiler .resume__button {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 16px; }
|
||||
#webprofiler .collectors {
|
||||
display: table;
|
||||
width: 100%; }
|
||||
#webprofiler .details {
|
||||
display: table-cell;
|
||||
overflow: hidden;
|
||||
padding: 16px;
|
||||
border-left: 1px solid rgba(0, 0, 0, 0.18); }
|
||||
#webprofiler .details__title {
|
||||
border-bottom: 1px solid #ccc;
|
||||
padding-bottom: 5px; }
|
||||
#webprofiler .details svg {
|
||||
max-width: 100%; }
|
||||
#webprofiler [class*='panel__filter--'] {
|
||||
display: inline-block;
|
||||
padding: 32px 16px 0 16px;
|
||||
position: relative; }
|
||||
#webprofiler .panel {
|
||||
margin: 0 0 20px;
|
||||
padding: 0;
|
||||
background: #fafafa;
|
||||
border: none; }
|
||||
#webprofiler .panel select,
|
||||
#webprofiler .panel input[type="text"] {
|
||||
min-width: 165px;
|
||||
font-size: 16px;
|
||||
background-color: #fafafa;
|
||||
border-color: transparent;
|
||||
border-width: 0;
|
||||
border-bottom: 2px solid #ffffff;
|
||||
padding: 8px 0;
|
||||
margin-bottom: 8px; }
|
||||
#webprofiler .panel select:focus,
|
||||
#webprofiler .panel input[type="text"]:focus {
|
||||
outline: none;
|
||||
border-bottom-color: #2979ff; }
|
||||
#webprofiler .panel select:focus + label,
|
||||
#webprofiler .panel input[type="text"]:focus + label {
|
||||
color: #2979ff; }
|
||||
#webprofiler .panel select {
|
||||
min-width: 165px;
|
||||
padding-right: 24px;
|
||||
border-radius: 0;
|
||||
background: url(../../images/caret-down.svg) no-repeat 99% 63%;
|
||||
-moz-appearance: none; }
|
||||
#webprofiler .panel select:hover {
|
||||
box-shadow: none; }
|
||||
#webprofiler .panel__title {
|
||||
font-size: 24px;
|
||||
line-height: 1;
|
||||
background-color: #f5f5f5;
|
||||
text-transform: none;
|
||||
padding: 16px;
|
||||
margin: -16px -16px 16px -16px; }
|
||||
#webprofiler .panel__toolbar {
|
||||
background-color: #fafafa;
|
||||
margin: -16px -16px 16px -16px; }
|
||||
#webprofiler .panel__filter-action {
|
||||
display: inline-block; }
|
||||
#webprofiler .panel__filter-label {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
margin-top: 16px; }
|
||||
#webprofiler .panel__container {
|
||||
background-color: #ffffff;
|
||||
position: relative;
|
||||
padding: 16px; }
|
||||
#webprofiler .panel__expand-content {
|
||||
display: none; }
|
||||
#webprofiler .is--open .panel__expand-content {
|
||||
display: block; }
|
||||
#webprofiler .is--hightlighted {
|
||||
border-right: 10px solid #aa00ff;
|
||||
padding-right: 6px; }
|
||||
#webprofiler .is--hidden {
|
||||
display: none; }
|
||||
#webprofiler .app {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
display: block;
|
||||
cursor: pointer; }
|
||||
#webprofiler .app__bar {
|
||||
padding: 16px; }
|
||||
#webprofiler .app__title {
|
||||
margin: 8px 32px 0 0;
|
||||
font-weight: normal; }
|
||||
#webprofiler .app__icon {
|
||||
float: right;
|
||||
height: 32px;
|
||||
width: 32px; }
|
||||
#webprofiler .app__container {
|
||||
padding: 16px; }
|
||||
#webprofiler .app__main-data {
|
||||
font-size: 24px;
|
||||
padding-bottom: 16px; }
|
||||
#webprofiler .app__secondary-data {
|
||||
font-size: 14px;
|
||||
padding-bottom: 16px;
|
||||
margin-top: -8px; }
|
||||
#webprofiler .ui--red .app__bar {
|
||||
background-color: #ff1744; }
|
||||
#webprofiler .ui--red .app__container {
|
||||
background-color: #ff8a80; }
|
||||
#webprofiler .ui--pink .app__bar {
|
||||
background-color: #f50057; }
|
||||
#webprofiler .ui--pink .app__container {
|
||||
background-color: #ff80ab; }
|
||||
#webprofiler .ui--purple .app__bar {
|
||||
background-color: #d500f9; }
|
||||
#webprofiler .ui--purple .app__container {
|
||||
background-color: #ea80fc; }
|
||||
#webprofiler .ui--deepPurple .app__bar {
|
||||
background-color: #651fff; }
|
||||
#webprofiler .ui--deepPurple .app__container {
|
||||
background-color: #b388ff; }
|
||||
#webprofiler .ui--indigo .app__bar {
|
||||
background-color: #3d5afe; }
|
||||
#webprofiler .ui--indigo .app__container {
|
||||
background-color: #8c9eff; }
|
||||
#webprofiler .ui--blue .app__bar {
|
||||
background-color: #2979ff; }
|
||||
#webprofiler .ui--blue .app__container {
|
||||
background-color: #82b1ff; }
|
||||
#webprofiler .ui--lightBlue .app__bar {
|
||||
background-color: #00b0ff; }
|
||||
#webprofiler .ui--lightBlue .app__container {
|
||||
background-color: #80d8ff; }
|
||||
#webprofiler .ui--cyan .app__bar {
|
||||
background-color: #00e5ff; }
|
||||
#webprofiler .ui--cyan .app__container {
|
||||
background-color: #84ffff; }
|
||||
#webprofiler .ui--teal .app__bar {
|
||||
background-color: #ade9b6; }
|
||||
#webprofiler .ui--teal .app__container {
|
||||
background-color: #a7ffeb; }
|
||||
#webprofiler .ui--green .app__bar {
|
||||
background-color: #00e676; }
|
||||
#webprofiler .ui--green .app__container {
|
||||
background-color: #b9f6ca; }
|
||||
#webprofiler .ui--lightGreen .app__bar {
|
||||
background-color: #76ff03; }
|
||||
#webprofiler .ui--lightGreen .app__container {
|
||||
background-color: #ccff90; }
|
||||
#webprofiler .ui--lime .app__bar {
|
||||
background-color: #c6ff00; }
|
||||
#webprofiler .ui--lime .app__container {
|
||||
background-color: #f4ff81; }
|
||||
#webprofiler .ui--yellow .app__bar {
|
||||
background-color: #ffea00; }
|
||||
#webprofiler .ui--yellow .app__container {
|
||||
background-color: #ffff8d; }
|
||||
#webprofiler .ui--amber .app__bar {
|
||||
background-color: #ffc400; }
|
||||
#webprofiler .ui--amber .app__container {
|
||||
background-color: #ffe57f; }
|
||||
#webprofiler .ui--orange .app__bar {
|
||||
background-color: #ff9100; }
|
||||
#webprofiler .ui--orange .app__container {
|
||||
background-color: #ffd180; }
|
||||
#webprofiler .ui--deepOrange .app__bar {
|
||||
background-color: #ff3d00; }
|
||||
#webprofiler .ui--deepOrange .app__container {
|
||||
background-color: #ff9e80; }
|
||||
#webprofiler .ui--brown .app__bar {
|
||||
background-color: #8d6e63; }
|
||||
#webprofiler .ui--brown .app__container {
|
||||
background-color: #d7ccc8; }
|
||||
#webprofiler .ui--gray .app__bar {
|
||||
background-color: #bdbdbd; }
|
||||
#webprofiler .ui--gray .app__container {
|
||||
background-color: #f5f5f5; }
|
||||
#webprofiler .ui--blueGray .app__bar {
|
||||
background-color: #bdbdbd; }
|
||||
#webprofiler .ui--blueGray .app__container {
|
||||
background-color: #f5f5f5; }
|
||||
#webprofiler .loader--fixed {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
width: 80%;
|
||||
height: 100px;
|
||||
zoom: 1.7;
|
||||
text-align: center; }
|
||||
#webprofiler .loader__circle {
|
||||
animation: rotate 2s linear infinite;
|
||||
height: 100px;
|
||||
position: relative;
|
||||
width: 100px; }
|
||||
#webprofiler .loader__path {
|
||||
stroke-dasharray: 1, 200;
|
||||
stroke-dashoffset: 0;
|
||||
animation: dash 1.5s ease-in-out infinite, color 6s ease-in-out infinite;
|
||||
stroke-linecap: round; }
|
||||
@keyframes rotate {
|
||||
100% {
|
||||
transform: rotate(360deg); } }
|
||||
@keyframes dash {
|
||||
0% {
|
||||
stroke-dasharray: 1, 200;
|
||||
stroke-dashoffset: 0; }
|
||||
50% {
|
||||
stroke-dasharray: 89, 200;
|
||||
stroke-dashoffset: -35; }
|
||||
100% {
|
||||
stroke-dasharray: 89, 200;
|
||||
stroke-dashoffset: -124; } }
|
||||
@keyframes color {
|
||||
100%, 0% {
|
||||
stroke: #ff1744; }
|
||||
40% {
|
||||
stroke: #2979ff; }
|
||||
66% {
|
||||
stroke: #00e676; }
|
||||
80%, 90% {
|
||||
stroke: #ffea00; } }
|
||||
#webprofiler .loader--linear {
|
||||
height: 3px;
|
||||
background-color: #ffea00;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0; }
|
||||
#webprofiler .loader__bar {
|
||||
content: "";
|
||||
display: inline;
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 100%;
|
||||
left: 50%;
|
||||
text-align: center; }
|
||||
#webprofiler .loader__bar:nth-child(1) {
|
||||
background-color: #ff1744;
|
||||
animation: loading 3s linear infinite; }
|
||||
#webprofiler .loader__bar:nth-child(2) {
|
||||
background-color: #2979ff;
|
||||
animation: loading 3s linear 1s infinite; }
|
||||
#webprofiler .loader__bar:nth-child(3) {
|
||||
background-color: #00e676;
|
||||
animation: loading 3s linear 2s infinite; }
|
||||
@keyframes loading {
|
||||
from {
|
||||
left: 50%;
|
||||
width: 0;
|
||||
z-index: 100; }
|
||||
33.3333% {
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 10; }
|
||||
to {
|
||||
left: 0;
|
||||
width: 100%; } }
|
||||
#webprofiler .modal {
|
||||
background-color: rgba(0, 0, 0, 0.87);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0; }
|
||||
#webprofiler .modal__container {
|
||||
border-top-right-radius: 2px;
|
||||
border-top-left-radius: 2px;
|
||||
box-shadow: 0 2px 4px -2px #000000;
|
||||
background-color: #ffffff;
|
||||
position: relative;
|
||||
width: 33%;
|
||||
min-width: 350px;
|
||||
margin: 0 auto;
|
||||
margin-top: 15%; }
|
||||
#webprofiler .modal__bar {
|
||||
padding: 16px; }
|
||||
#webprofiler .modal__content {
|
||||
padding: 16px;
|
||||
font-size: 14px; }
|
||||
#webprofiler .modal__title {
|
||||
margin: 8px 32px 0 0;
|
||||
font-weight: normal;
|
||||
font-size: 24px; }
|
||||
#webprofiler .modal__main-data {
|
||||
font-size: 24px;
|
||||
padding-bottom: 16px; }
|
||||
#webprofiler .modal textarea {
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
word-break: break-word;
|
||||
font-size: 14px;
|
||||
background-color: #f5f5f5;
|
||||
max-width: 100%;
|
||||
height: 320px;
|
||||
width: 100%;
|
||||
padding: 16px;
|
||||
box-sizing: border-box;
|
||||
border: none; }
|
||||
#webprofiler .t--heading, #webprofiler .tabs__radio:checked:nth-child(1) ~ .tabs__tabs li:nth-child(1) .tabs__label, #webprofiler .tabs__radio:checked:nth-child(2) ~ .tabs__tabs li:nth-child(2) .tabs__label, #webprofiler .tabs__radio:checked:nth-child(3) ~ .tabs__tabs li:nth-child(3) .tabs__label, #webprofiler .overview__title, #webprofiler .resume__title, #webprofiler .panel select,
|
||||
#webprofiler .panel input[type="text"], #webprofiler .panel__title {
|
||||
color: black; }
|
||||
#webprofiler .t--notice, #webprofiler .tabs__label, #webprofiler .overview__subtitle, #webprofiler .resume__subtitle,
|
||||
#webprofiler .resume__time, #webprofiler .panel__filter-label {
|
||||
color: rgba(0, 0, 0, 0.3); }
|
||||
#webprofiler .l-card {
|
||||
width: 32%;
|
||||
margin-left: 1%;
|
||||
margin-right: 1%;
|
||||
float: left; }
|
||||
#webprofiler .l-card:nth-child(3n + 1) {
|
||||
margin-left: 0; }
|
||||
#webprofiler .l-card:nth-child(3n) {
|
||||
margin-right: 0; }
|
||||
#webprofiler .l-left {
|
||||
float: left; }
|
||||
#webprofiler .l-right {
|
||||
float: right; }
|
||||
#webprofiler .h--word-broken {
|
||||
word-break: break-all; }
|
||||
#webprofiler .h--word-intact {
|
||||
word-break: normal; }
|
||||
|
||||
/*# sourceMappingURL=dashboard.css.map */
|
||||
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* Timeline
|
||||
*/
|
||||
|
||||
.timeline__legends {
|
||||
font-size: 12px;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
.timeline__legends span {
|
||||
border-left-width: 10px;
|
||||
border-left-style: solid;
|
||||
padding: 0 10px 0 5px;
|
||||
}
|
||||
|
||||
.timeline__legends--default {
|
||||
border-left-color: #E91E63;
|
||||
}
|
||||
|
||||
.timeline__legends--section {
|
||||
border-left-color: #3F51B5;
|
||||
}
|
||||
|
||||
.timeline__legends--event_listener {
|
||||
border-left-color: #00BCD4;
|
||||
}
|
||||
|
||||
.timeline__legends--event_listener_loading {
|
||||
border-left-color: #8BC34A;
|
||||
}
|
||||
|
||||
.timeline__legends--template {
|
||||
border-left-color: #FFC107;
|
||||
}
|
||||
|
||||
.timeline__legends--service {
|
||||
border-left-color: #795548;
|
||||
}
|
||||
|
||||
text {
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
height: 22px;
|
||||
fill: rgba(0, 0, 0, 0.87);
|
||||
}
|
||||
#timeline {
|
||||
background: white;
|
||||
margin: 10px 0;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
padding: 0 0 40px 0;
|
||||
}
|
||||
.timeline__row,
|
||||
.timeline__scale--x,
|
||||
.timeline__label rect {
|
||||
stroke: rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
.timeline__row{
|
||||
fill: transparent;
|
||||
}
|
||||
.timeline__label rect {
|
||||
fill: white;
|
||||
}
|
||||
.timeline__period--default {
|
||||
fill: #E91E63;
|
||||
}
|
||||
.timeline__period--service {
|
||||
fill: #795548;
|
||||
}
|
||||
.timeline__period--section {
|
||||
fill: #3F51B5;
|
||||
}
|
||||
.timeline__period--event_listener {
|
||||
fill: #00BCD4;
|
||||
}
|
||||
.timeline__period--event_listener_loading {
|
||||
fill: #8BC34A;
|
||||
}
|
||||
.timeline__period--template {
|
||||
fill: #FFC107;
|
||||
}
|
||||
.timeline__period-trigger{
|
||||
fill: transparent;
|
||||
}
|
||||
.tooltip{
|
||||
position: absolute;
|
||||
padding: 8px;
|
||||
background: rgb(0,0,0);
|
||||
background: rgba(0, 0, 0, 0.87);
|
||||
border-bottom-left-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
width: 175px;
|
||||
text-align: center;
|
||||
display: none;
|
||||
color: white;
|
||||
}
|
||||
.tooltip__title{
|
||||
display: block;
|
||||
}
|
||||
.tooltip__content{
|
||||
display: block;
|
||||
}
|
||||
.axis{
|
||||
stroke-width: 2px;
|
||||
fill: none;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#333333" d="M3.8 5.4c-.165-.22-.075-.4.2-.4h8.002c.275 0 .365.18.199.4l-3.898 5.2c-.166.221-.436.221-.6 0l-3.903-5.2z"/></svg>
|
||||
|
After Width: | Height: | Size: 202 B |
@@ -0,0 +1,35 @@
|
||||
(function ($, Drupal, drupalSettings, Backbone) {
|
||||
|
||||
"use strict";
|
||||
|
||||
Drupal.webprofiler.collectors.Collectors = Backbone.Collection.extend({
|
||||
model: Drupal.webprofiler.models.Collector,
|
||||
|
||||
url: Drupal.url('admin/reports/profiler/view/' + drupalSettings.webprofiler.token + '/collectors'),
|
||||
|
||||
/**
|
||||
* Unselect all models.
|
||||
*/
|
||||
resetSelected: function () {
|
||||
this.each(function (model) {
|
||||
model.set({"selected": false});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Select a specific model from the collection.
|
||||
*
|
||||
* @param id
|
||||
* @returns {*}
|
||||
*/
|
||||
selectByID: function (id) {
|
||||
this.resetSelected();
|
||||
var collector = this.get(id);
|
||||
collector.set({"selected": true});
|
||||
|
||||
return collector.id;
|
||||
}
|
||||
});
|
||||
|
||||
}(jQuery, Drupal, drupalSettings, Backbone));
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
(function (drupalSettings) {
|
||||
|
||||
Drupal.webprofiler.helpers = (function () {
|
||||
|
||||
"use strict";
|
||||
|
||||
var escapeRx = function escapeRegExp(string) {
|
||||
return string.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
|
||||
},
|
||||
|
||||
repl = function replaceAll(string, find, replace) {
|
||||
return string.replace(new RegExp(escapeRx(find), 'g'), replace);
|
||||
},
|
||||
|
||||
shortLink = function (clazz) {
|
||||
if (!clazz) {
|
||||
return null;
|
||||
}
|
||||
clazz = repl(clazz, '/', '\\');
|
||||
var parts = clazz.split("\\"), result = [], size = (parts.length - 1);
|
||||
|
||||
_.each(parts, function (item, key) {
|
||||
if (key < size) {
|
||||
result.push(item.substring(0, 1));
|
||||
} else {
|
||||
result.push(item);
|
||||
}
|
||||
});
|
||||
return result.join("\\");
|
||||
},
|
||||
|
||||
abbr = function (clazz) {
|
||||
if (!clazz) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return '<abbr title="' + clazz + '">' + shortLink(clazz) + '</abbr>';
|
||||
},
|
||||
|
||||
ideLink = function (file, line) {
|
||||
if (!file) {
|
||||
return null;
|
||||
}
|
||||
|
||||
line = line || 0;
|
||||
|
||||
file = file.replace(drupalSettings.webprofiler.ide_link_remote, drupalSettings.webprofiler.ide_link_local);
|
||||
|
||||
return drupalSettings.webprofiler.ide_link.replace("@file", file).replace("@line", line);
|
||||
},
|
||||
|
||||
classLink = function (data) {
|
||||
var link = ideLink(data['file'], data['line']), clazz = abbr(data['class']), method = data['method'], output = '';
|
||||
|
||||
output = clazz;
|
||||
if (method) {
|
||||
output += '::' + method;
|
||||
}
|
||||
|
||||
if (link) {
|
||||
output = '<a href="' + link + '">' + output + '</a>';
|
||||
}
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
printTime = function (data, unit) {
|
||||
unit = unit || 'ms';
|
||||
data = Math.round((data + 0.00001) * 100) / 100;
|
||||
return data + ' ' + unit;
|
||||
},
|
||||
|
||||
frm = function (obj, level) {
|
||||
level = level || 0;
|
||||
var str = '<ul class="list--unstyled list--level-' + level + ' list--flat">', prop;
|
||||
if (typeof obj != 'object') {
|
||||
return obj;
|
||||
}
|
||||
for (prop in obj) {
|
||||
if (isInt(prop)) {
|
||||
str += '<li>' + frm(obj[prop], level + 1) + '</li>';
|
||||
} else {
|
||||
str += '<li><span class="list-item--bold">' + prop + '</span>: ' + frm(obj[prop], level + 1) + '</li>';
|
||||
}
|
||||
}
|
||||
return str + '</ul>';
|
||||
},
|
||||
|
||||
isInt = function (value) {
|
||||
var x;
|
||||
return isNaN(value) ? !1 : (x = parseFloat(value), (0 | x) === x);
|
||||
};
|
||||
|
||||
return {
|
||||
frm: frm,
|
||||
ideLink: ideLink,
|
||||
shortLink: shortLink,
|
||||
classLink: classLink,
|
||||
printTime: printTime
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
}(drupalSettings));
|
||||
@@ -0,0 +1,105 @@
|
||||
(function ($, Drupal, Backbone) {
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Define namespaces.
|
||||
*/
|
||||
Drupal.webprofiler = {
|
||||
views: {},
|
||||
models: {},
|
||||
collectors: {},
|
||||
routers: {}
|
||||
};
|
||||
|
||||
Drupal.behaviors.webprofiler = {
|
||||
attach: function (context) {
|
||||
var el,
|
||||
elz,
|
||||
key,
|
||||
sel,
|
||||
value,
|
||||
select,
|
||||
selector,
|
||||
unselected,
|
||||
filter = [],
|
||||
|
||||
livefilter = function (e) {
|
||||
el = $(e).attr('id').replace('edit-', '');
|
||||
value = $(e).val();
|
||||
filter[el] = value.replace('/', '\/');
|
||||
selector = [];
|
||||
unselected = [];
|
||||
|
||||
for (key in filter) {
|
||||
if (filter[key].length > 0 && filter[key] != ' ') {
|
||||
select = filter[key].split(' ').filter(Boolean);
|
||||
for (sel in select) {
|
||||
selector.push('[data-wp-' + key + ' *= ' + select[sel].toLowerCase() + ']');
|
||||
unselected.push('[data-wp-' + key + ']:not([data-wp-' + key + ' *= ' + select[sel].toLowerCase() + '])');
|
||||
}
|
||||
}
|
||||
else {
|
||||
selector.push('[data-wp-' + key + ']');
|
||||
}
|
||||
}
|
||||
for (elz in unselected) {
|
||||
$(unselected[elz]).addClass('is--hidden');
|
||||
}
|
||||
$(selector.join('')).removeClass('is--hidden');
|
||||
},
|
||||
|
||||
modalFill = function(t,c){
|
||||
$('.modal__title').html(t);
|
||||
$('.modal__main-data').html(c);
|
||||
},
|
||||
|
||||
clipboard = function (e, t) {
|
||||
var clip = e.parent().find(t).get(0),
|
||||
title = 'Original Code',
|
||||
content = '<textarea readonly >' +
|
||||
clip.textContent +
|
||||
'</textarea>';
|
||||
|
||||
modalFill(title,content);
|
||||
$('.modal').show();
|
||||
};
|
||||
|
||||
$(context).find('#collectors').once('webprofiler').each(function () {
|
||||
new Drupal.webprofiler.routers.CollectorsRouter({el: $('#collectors')});
|
||||
Backbone.history.start({
|
||||
pushState: false
|
||||
});
|
||||
});
|
||||
|
||||
$(context).find('.js--modal-close').each(function () {
|
||||
$(this).on('click', function () {
|
||||
$('.js--modal').hide();
|
||||
});
|
||||
});
|
||||
|
||||
$(context).find('.js--live-filter').each(function () {
|
||||
$(this).on('keyup', function () {
|
||||
livefilter($(this));
|
||||
});
|
||||
$(this).on('change', function () {
|
||||
livefilter($(this));
|
||||
});
|
||||
});
|
||||
|
||||
$(context).find('.js--panel-toggle').once('js--panel-toggle').each(function () {
|
||||
$(this).on('click', function () {
|
||||
$(this).parent().parent().toggleClass('is--open');
|
||||
});
|
||||
});
|
||||
|
||||
$(context).find('.js--clipboard-trigger').once('js--clipboard-trigger').each(function () {
|
||||
$(this).on('click', function () {
|
||||
clipboard($(this), '.js--clipboard-target')
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
}(jQuery, Drupal, Backbone));
|
||||
@@ -0,0 +1,15 @@
|
||||
(function ($, Drupal, drupalSettings, Backbone) {
|
||||
|
||||
"use strict";
|
||||
|
||||
Drupal.webprofiler.models.Collector = Backbone.Model.extend({
|
||||
idAttribute: 'name',
|
||||
urlRoot: Drupal.url('admin/reports/profiler/view/' + drupalSettings.webprofiler.token + '/collectors'),
|
||||
defaults: {
|
||||
name: "default",
|
||||
data: [],
|
||||
selected: false
|
||||
}
|
||||
});
|
||||
|
||||
}(jQuery, Drupal, drupalSettings, Backbone));
|
||||
@@ -0,0 +1,48 @@
|
||||
(function ($, Drupal, drupalSettings, Backbone) {
|
||||
|
||||
"use strict";
|
||||
|
||||
var collectors = new Drupal.webprofiler.collectors.Collectors(drupalSettings.webprofiler.collectors);
|
||||
|
||||
Drupal.webprofiler.routers.CollectorsRouter = Backbone.Router.extend({
|
||||
routes: {
|
||||
':id': 'selectCollector'
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param id
|
||||
*/
|
||||
selectCollector: function (id) {
|
||||
var collectors = this.collectors, layout = this.layout;
|
||||
|
||||
//collectors.resetSelected();
|
||||
collectors.selectByID(id);
|
||||
|
||||
var collector = collectors.get(id);
|
||||
|
||||
if (collector.get('data').length != 0) {
|
||||
layout.setDetails(collector);
|
||||
} else {
|
||||
var deferred = collectors.get(id).fetch();
|
||||
deferred.done(function () {
|
||||
layout.setDetails(collector);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param options
|
||||
*/
|
||||
initialize: function (options) {
|
||||
this.collectors = collectors;
|
||||
this.layout = Drupal.webprofiler.views.Layout.getInstance({
|
||||
el: options.el,
|
||||
router: this
|
||||
});
|
||||
this.layout.render();
|
||||
}
|
||||
});
|
||||
|
||||
}(jQuery, Drupal, drupalSettings, Backbone));
|
||||
@@ -0,0 +1,29 @@
|
||||
(function ($, Drupal, Backbone) {
|
||||
|
||||
"use strict";
|
||||
|
||||
Drupal.webprofiler.views.CollectorView = Backbone.View.extend({
|
||||
tagName: 'li',
|
||||
template: _.template($("script#collector").html()),
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
initialize: function () {
|
||||
_.bindAll(this, "render");
|
||||
this.listenTo(this.model, 'change:selected', this.render);
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {Drupal.webprofiler.views.CollectorView}
|
||||
*/
|
||||
render: function () {
|
||||
this.$el.html(this.template(this.model.toJSON()));
|
||||
this.$el.toggleClass('is--selected', this.model.get('selected'));
|
||||
return this;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}(jQuery, Drupal, Backbone));
|
||||
@@ -0,0 +1,21 @@
|
||||
(function ($, Drupal, Backbone) {
|
||||
|
||||
"use strict";
|
||||
|
||||
Drupal.webprofiler.views.CollectorsList = Backbone.View.extend({
|
||||
tagName: 'section',
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {Drupal.webprofiler.views.CollectorsList}
|
||||
*/
|
||||
render: function () {
|
||||
var collectorsView = this.collection.map(function (collector) {
|
||||
return (new Drupal.webprofiler.views.CollectorView({model: collector})).render().el;
|
||||
});
|
||||
this.$el.html(collectorsView);
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
}(jQuery, Drupal, Backbone));
|
||||
@@ -0,0 +1,20 @@
|
||||
(function ($, Drupal, Backbone) {
|
||||
|
||||
"use strict";
|
||||
|
||||
Drupal.webprofiler.views.DetailsView = Backbone.View.extend({
|
||||
el: '#details',
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {Drupal.webprofiler.views.DetailsView}
|
||||
*/
|
||||
render: function () {
|
||||
var template = _.template($("script#" + this.model.get('name')).html());
|
||||
|
||||
this.$el.html(template(this.model.toJSON()));
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
}(jQuery, Drupal, Backbone));
|
||||
@@ -0,0 +1,83 @@
|
||||
(function ($, Drupal, drupalSettings, Backbone) {
|
||||
|
||||
"use strict";
|
||||
|
||||
Drupal.webprofiler.views.Layout = Backbone.View.extend({
|
||||
template: _.template(
|
||||
'<div id="overview" class="overview">' +
|
||||
'<ul class="overview__list"></ul>' +
|
||||
'</div>' +
|
||||
'<div id="details" class="details">' +
|
||||
'Choose a collector.' +
|
||||
'</div>'
|
||||
),
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {Drupal.webprofiler.views.Layout}
|
||||
*/
|
||||
render: function () {
|
||||
this.$el.html(this.template());
|
||||
|
||||
if(this.currentDetails) {
|
||||
this.currentDetails.setElement(this.$('#details')).render();
|
||||
}
|
||||
|
||||
this.overview.setElement(this.$('#overview ul')).render();
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param options
|
||||
*/
|
||||
initialize: function (options) {
|
||||
options.router.collectors.on('request', this.beginSync);
|
||||
options.router.collectors.on('sync', this.finishSync);
|
||||
|
||||
this.overview = new Drupal.webprofiler.views.CollectorsList({
|
||||
collection: options.router.collectors,
|
||||
router: options.router
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param collector
|
||||
*/
|
||||
setDetails: function (collector) {
|
||||
if (this.currentDetails) this.currentDetails.remove();
|
||||
this.currentDetails = new Drupal.webprofiler.views.DetailsView({model: collector});
|
||||
this.render();
|
||||
Drupal.attachBehaviors(/*this.$el, drupalSettings*/);
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
beginSync: function () {
|
||||
$('.loader--fixed').fadeIn({duration: 100});
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
finishSync: function () {
|
||||
$('.loader--fixed').fadeOut({duration: 100});
|
||||
}
|
||||
});
|
||||
|
||||
var instance;
|
||||
Drupal.webprofiler.views.Layout.getInstance = function (options) {
|
||||
if (!instance) {
|
||||
instance = new Drupal.webprofiler.views.Layout({
|
||||
el: options.el,
|
||||
router: options.router,
|
||||
collection: options.router.collectors
|
||||
});
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
}(jQuery, Drupal, drupalSettings, Backbone));
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* @file
|
||||
* Database panel app.
|
||||
*/
|
||||
(function ($, Drupal, drupalSettings) {
|
||||
|
||||
"use strict";
|
||||
|
||||
Drupal.behaviors.webprofiler_database = {
|
||||
attach: function (context) {
|
||||
$(context).find('.js--explain-trigger').once('js--explain-trigger').each(function () {
|
||||
|
||||
$(this).on('click', function () {
|
||||
var position = $(this).attr('data-wp-queryPosition'),
|
||||
wrapper = $(this).parent().parent().find('.js--explain-target'),
|
||||
loader = $(this).parent().parent().find('.js--loader');
|
||||
|
||||
if (wrapper.html().length === 0) {
|
||||
|
||||
var url = Drupal.url('admin/reports/profiler/database_explain/' + drupalSettings.webprofiler.token + '/' + position);
|
||||
|
||||
loader.show();
|
||||
|
||||
$.getJSON(url, function (data) {
|
||||
_.templateSettings.variable = 'rc';
|
||||
var template = _.template(
|
||||
$("#wp-query-explain-template").html()
|
||||
);
|
||||
wrapper.html(template(data));
|
||||
loader.hide();
|
||||
delete _.templateSettings.variable;
|
||||
});
|
||||
}
|
||||
wrapper.toggle();
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
$(context).find('.js--code-toggle').once('js--code-toggle').each(function () {
|
||||
$(this).on('click', function () {
|
||||
$(this).parent().find('.js--code-target').find('code').toggleClass('is--hidden');
|
||||
});
|
||||
});
|
||||
|
||||
$(context).find('.js--code-toggle--global').once('js--code-toggle--global').each(function () {
|
||||
$(this).on('click', function () {
|
||||
|
||||
if($(this).hasClass('js--placeholder-visible')){
|
||||
$('.js--placeholder-query').addClass('is--hidden');
|
||||
$('.js--original-query').removeClass('is--hidden');
|
||||
|
||||
}else{
|
||||
$('.js--placeholder-query').removeClass('is--hidden');
|
||||
$('.js--original-query').addClass('is--hidden');
|
||||
}
|
||||
$(this).toggleClass('js--placeholder-visible');
|
||||
});
|
||||
});
|
||||
|
||||
if (typeof hljs != "undefined") {
|
||||
$('code.sql').each(function (i, block) {
|
||||
hljs.highlightBlock(block);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
(jQuery, Drupal, drupalSettings);
|
||||
@@ -0,0 +1,223 @@
|
||||
/**
|
||||
* @file
|
||||
* Timeline panel app.
|
||||
*/
|
||||
(function ($, Drupal, drupalSettings, d3) {
|
||||
|
||||
'use strict';
|
||||
|
||||
Drupal.behaviors.webprofiler_timeline = {
|
||||
attach: function (context) {
|
||||
if (typeof d3 != 'undefined') {
|
||||
|
||||
// data
|
||||
var data = drupalSettings.webprofiler.time.events;
|
||||
var parts = [];
|
||||
var dataL = data.length;
|
||||
var perL;
|
||||
var labelW = [];
|
||||
var rowW;
|
||||
var scalePadding;
|
||||
var endTime = parseInt(data[(dataL - 1)].endtime);
|
||||
var roundTime = Math.ceil(endTime / 1000) * 1000;
|
||||
var endScale;
|
||||
|
||||
for (var j = 0; j < dataL; j++) {
|
||||
perL = data[j].periods.length;
|
||||
for (var k = 0; k < perL; k++) {
|
||||
parts.push({
|
||||
lane: j,
|
||||
category: data[j].category,
|
||||
memory: data[j].memory,
|
||||
name: data[j].name,
|
||||
start: data[j].periods[k].start,
|
||||
end: data[j].periods[k].end
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var tooltipCtrl = function (d, i) {
|
||||
tooltip.html('<span class="tooltip__content">memory usage: ' + d.memory + '</span>' +
|
||||
'<span class="tooltip__content">' + parseInt(d.start) + 'ms ~ ' + parseInt(d.end) + 'ms</span>');
|
||||
tooltip
|
||||
.style('display', 'block')
|
||||
.style('left', (d3.event.layerX - 87) + 'px')
|
||||
.style('top', ((d.lane + 1) * 22) + 'px')
|
||||
.style('opacity', .9);
|
||||
};
|
||||
|
||||
var xscale = d3.scale.linear().domain([0, roundTime]).range([0, 1000]);
|
||||
d3.select('#timeline').append('svg').attr('height', (dataL + 1) * 22 + 'px').attr('width', '100%').attr('class', 'timeline__canvas');
|
||||
|
||||
// tooltips
|
||||
var tooltip = d3.select('#timeline')
|
||||
.append('div')
|
||||
.attr('class', 'tooltip');
|
||||
|
||||
|
||||
// Add a rectangle for every data element.
|
||||
d3.select('.timeline__canvas')
|
||||
.append('g')
|
||||
.attr('class', 'timeline__rows')
|
||||
.attr('x', 0)
|
||||
.attr('y', 0)
|
||||
.selectAll('g')
|
||||
.data(data)
|
||||
.enter()
|
||||
.append('rect')
|
||||
.attr('class', 'timeline__row')
|
||||
.attr('x', 0)
|
||||
.attr('y', function (d, i) {
|
||||
return (i * 22);
|
||||
})
|
||||
.attr('height', 22)
|
||||
.attr('width', '100%')
|
||||
.each(function () {
|
||||
rowW = this.getBoundingClientRect().width;
|
||||
});
|
||||
|
||||
// scale
|
||||
var scale = d3.select('.timeline__canvas')
|
||||
.append('g')
|
||||
.attr('class', 'timeline__scale')
|
||||
.attr('id', 'timeline__scale')
|
||||
.attr('x', 0)
|
||||
.attr('y', 0)
|
||||
.selectAll('g')
|
||||
.data(data)
|
||||
.enter()
|
||||
.append('a')
|
||||
.attr('xlink:href', function (d) {
|
||||
return Drupal.webprofiler.helpers.ideLink(d.link);
|
||||
})
|
||||
.attr('class', function (d) {
|
||||
return 'timeline__label ' + d.category;
|
||||
})
|
||||
.attr('x', xscale(5))
|
||||
.attr('y', function (d, i) {
|
||||
return (((i + 1) * 22) - 5);
|
||||
});
|
||||
|
||||
scale.append('title')
|
||||
.text(function (d) {
|
||||
return d.name;
|
||||
});
|
||||
|
||||
scale.append('text')
|
||||
.attr('x', xscale(5))
|
||||
.attr('y', function (d, i) {
|
||||
return (((i + 1) * 22) - 5);
|
||||
})
|
||||
.text(function (d) {
|
||||
return Drupal.webprofiler.helpers.shortLink(d.name);
|
||||
})
|
||||
.each(function (d) {
|
||||
labelW.push(this.getBoundingClientRect().width);
|
||||
});
|
||||
|
||||
scalePadding = Math.max.apply(null, labelW) + 10;
|
||||
|
||||
scale.insert('rect', 'title')
|
||||
.attr('x', 0)
|
||||
.attr('y', function (d, i) {
|
||||
return (i * 22);
|
||||
})
|
||||
.attr('height', 22)
|
||||
.attr('stroke', 'transparent')
|
||||
.attr('strokw-width', 1)
|
||||
.attr('width', scalePadding);
|
||||
|
||||
// times
|
||||
var events = d3.select('.timeline__canvas')
|
||||
.insert('g', '.timeline__scale')
|
||||
.attr('class', 'timeline__parts')
|
||||
.attr('x', 0)
|
||||
.attr('y', 0)
|
||||
.selectAll('g')
|
||||
.data(parts)
|
||||
.enter();
|
||||
|
||||
events.append('rect').attr('class', function (d) {
|
||||
return 'timeline__period--' + d.category;
|
||||
})
|
||||
.attr('x', function (d) {
|
||||
return xscale(parseInt(d.start)) + scalePadding;
|
||||
})
|
||||
.attr('y', function (d) {
|
||||
return d.lane * 22;
|
||||
})
|
||||
.attr('height', 22)
|
||||
.attr('width', function (d) {
|
||||
return xscale(Math.max(parseInt(d.end - d.start), 1));
|
||||
});
|
||||
|
||||
events.append('rect')
|
||||
.attr('class', function (d) {
|
||||
return 'timeline__period-trigger';
|
||||
})
|
||||
.attr('x', function (d) {
|
||||
return xscale(parseInt(d.start)) + scalePadding - 5;
|
||||
})
|
||||
.attr('y', function (d) {
|
||||
return d.lane * 22;
|
||||
})
|
||||
.attr('height', 22)
|
||||
.attr('width', function (d) {
|
||||
return xscale(Math.max(parseInt(d.end - d.start), 1)) + 11;
|
||||
})
|
||||
.on('mouseover', function (d, i) {
|
||||
tooltipCtrl(d, i);
|
||||
})
|
||||
.on('mouseout', function (d) {
|
||||
tooltip
|
||||
.style('display', 'none');
|
||||
});
|
||||
|
||||
// Draw X-axis grid lines
|
||||
d3.select('.timeline__parts').insert('g', '.timeline__parts')
|
||||
.selectAll('line')
|
||||
.data(xscale.ticks(10))
|
||||
.enter()
|
||||
.append('line')
|
||||
.attr('class', 'timeline__scale--x')
|
||||
.attr('x1', xscale)
|
||||
.attr('x2', xscale)
|
||||
.attr('y1', 0)
|
||||
.attr('y2', data.length * 22)
|
||||
.attr('transform', 'translate( ' + scalePadding + ' , 0)');
|
||||
|
||||
var xAxis = d3.svg.axis().scale(xscale).ticks(10).orient('bottom').tickFormat(function (d) {
|
||||
return d + ' ms';
|
||||
});
|
||||
|
||||
d3.select('.timeline__parts').insert('g', '.timeline__parts')
|
||||
.attr('class', 'axis')
|
||||
.attr('transform', 'translate(' + scalePadding + ', ' + dataL * 22 + ')')
|
||||
.call(xAxis);
|
||||
|
||||
endScale = xscale(endTime) - rowW - parseInt(scalePadding);
|
||||
|
||||
if (parseInt(xscale(endTime)) > (rowW - parseInt(scalePadding))) {
|
||||
d3.select('.timeline__canvas')
|
||||
.call(
|
||||
d3.behavior.zoom()
|
||||
.scaleExtent([1, 1])
|
||||
.x(xscale)
|
||||
.on('zoom', function () {
|
||||
|
||||
var t = d3.event.translate;
|
||||
var tx = t[0];
|
||||
|
||||
tx = tx > 0 ? 0 : tx;
|
||||
|
||||
tx = tx < endScale ? endScale : tx;
|
||||
|
||||
d3.select('.timeline__parts').attr('transform', 'translate( ' + tx + ' , 0)');
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery, Drupal, drupalSettings, d3);
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Access;
|
||||
|
||||
use Drupal\Core\Access\AccessException;
|
||||
use Drupal\Core\Access\AccessManager;
|
||||
use Drupal\Component\Utility\ArgumentsResolverInterface;
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Access\AccessResultInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\webprofiler\DataCollector\RequestDataCollector;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Attaches access check services to routes and runs them on request.
|
||||
*
|
||||
* @see \Drupal\Tests\Core\Access\AccessManagerTest
|
||||
*/
|
||||
class AccessManagerWrapper extends AccessManager {
|
||||
|
||||
/**
|
||||
* @var \Drupal\webprofiler\DataCollector\RequestDataCollector
|
||||
*/
|
||||
private $dataCollector;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function check(RouteMatchInterface $route_match, AccountInterface $account = NULL, Request $request = NULL, $return_as_object = FALSE) {
|
||||
if (!isset($account)) {
|
||||
$account = $this->currentUser;
|
||||
}
|
||||
$route = $route_match->getRouteObject();
|
||||
$checks = $route->getOption('_access_checks') ?: array();
|
||||
|
||||
// Filter out checks which require the incoming request.
|
||||
if (!isset($request)) {
|
||||
$checks = array_diff($checks, $this->checkProvider->getChecksNeedRequest());
|
||||
}
|
||||
|
||||
$result = AccessResult::neutral();
|
||||
if (!empty($checks)) {
|
||||
$arguments_resolver = $this->argumentsResolverFactory->getArgumentsResolver($route_match, $account, $request);
|
||||
|
||||
if (!$checks) {
|
||||
return AccessResult::neutral();
|
||||
}
|
||||
$result = AccessResult::allowed();
|
||||
foreach ($checks as $service_id) {
|
||||
$result = $result->andIf($this->performCheck($service_id, $arguments_resolver, $request));
|
||||
}
|
||||
}
|
||||
return $return_as_object ? $result : $result->isAllowed();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function performCheck($service_id, ArgumentsResolverInterface $arguments_resolver, Request $request = NULL) {
|
||||
$callable = $this->checkProvider->loadCheck($service_id);
|
||||
$arguments = $arguments_resolver->getArguments($callable);
|
||||
/** @var \Drupal\Core\Access\AccessResultInterface $service_access **/
|
||||
$service_access = call_user_func_array($callable, $arguments);
|
||||
|
||||
if (!$service_access instanceof AccessResultInterface) {
|
||||
throw new AccessException("Access error in $service_id. Access services must return an object that implements AccessResultInterface.");
|
||||
}
|
||||
|
||||
if($request) {
|
||||
$this->dataCollector->addAccessCheck($service_id, $callable, $request);
|
||||
}
|
||||
|
||||
return $service_access;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Drupal\webprofiler\DataCollector\RequestDataCollector $dataCollector
|
||||
*/
|
||||
public function setDataCollector(RequestDataCollector $dataCollector) {
|
||||
$this->dataCollector = $dataCollector;
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Asset;
|
||||
|
||||
use Drupal\Core\Asset\AssetCollectionRendererInterface;
|
||||
use Drupal\webprofiler\DataCollector\AssetsDataCollector;
|
||||
|
||||
/**
|
||||
* Class CssCollectionRendererWrapper.
|
||||
*/
|
||||
class CssCollectionRendererWrapper implements AssetCollectionRendererInterface {
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Asset\AssetCollectionRendererInterface
|
||||
*/
|
||||
private $assetCollectionRenderer;
|
||||
|
||||
/**
|
||||
* @var \Drupal\webprofiler\DataCollector\AssetsDataCollector
|
||||
*/
|
||||
private $dataCollector;
|
||||
|
||||
/**
|
||||
* @param \Drupal\Core\Asset\AssetCollectionRendererInterface $assetCollectionRenderer
|
||||
* @param \Drupal\webprofiler\DataCollector\AssetsDataCollector $dataCollector
|
||||
*/
|
||||
public function __construct(AssetCollectionRendererInterface $assetCollectionRenderer, AssetsDataCollector $dataCollector) {
|
||||
$this->assetCollectionRenderer = $assetCollectionRenderer;
|
||||
$this->dataCollector = $dataCollector;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function render(array $css_assets) {
|
||||
$this->dataCollector->addCssAsset($css_assets);
|
||||
|
||||
return $this->assetCollectionRenderer->render($css_assets);
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Asset;
|
||||
|
||||
use Drupal\Core\Asset\AssetCollectionRendererInterface;
|
||||
use Drupal\webprofiler\DataCollector\AssetsDataCollector;
|
||||
|
||||
/**
|
||||
* Class JsCollectionRendererWrapper.
|
||||
*/
|
||||
class JsCollectionRendererWrapper implements AssetCollectionRendererInterface {
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Asset\AssetCollectionRendererInterface
|
||||
*/
|
||||
private $assetCollectionRenderer;
|
||||
|
||||
/**
|
||||
* @var \Drupal\webprofiler\DataCollector\AssetsDataCollector
|
||||
*/
|
||||
private $dataCollector;
|
||||
|
||||
/**
|
||||
* @param \Drupal\Core\Asset\AssetCollectionRendererInterface $assetCollectionRenderer
|
||||
* @param \Drupal\webprofiler\DataCollector\AssetsDataCollector $dataCollector
|
||||
*/
|
||||
public function __construct(AssetCollectionRendererInterface $assetCollectionRenderer, AssetsDataCollector $dataCollector) {
|
||||
$this->assetCollectionRenderer = $assetCollectionRenderer;
|
||||
$this->dataCollector = $dataCollector;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function render(array $js_assets) {
|
||||
$this->dataCollector->addJsAsset($js_assets);
|
||||
|
||||
return $this->assetCollectionRenderer->render($js_assets);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Cache;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\webprofiler\DataCollector\CacheDataCollector;
|
||||
|
||||
/**
|
||||
* Wraps an existing cache backend to track calls to the cache backend.
|
||||
*/
|
||||
class CacheBackendWrapper implements CacheBackendInterface {
|
||||
|
||||
/**
|
||||
* The data collector to register the calls.
|
||||
*
|
||||
* @var \Drupal\webprofiler\DataCollector\CacheDataCollector
|
||||
*/
|
||||
protected $cacheDataCollector;
|
||||
|
||||
/**
|
||||
* The wrapped cache backend.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\CacheBackendInterface
|
||||
*/
|
||||
protected $cacheBackend;
|
||||
|
||||
/**
|
||||
* The name of the wrapped cache bin.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $bin;
|
||||
|
||||
/**
|
||||
* Constructs a new CacheBackendWrapper.
|
||||
*
|
||||
* @param \Drupal\webprofiler\DataCollector\CacheDataCollector $cacheDataCollector
|
||||
* The cache data collector to inform about cache get calls.
|
||||
* @param \Drupal\Core\Cache\CacheBackendInterface $cacheBackend
|
||||
* The wrapped cache backend.
|
||||
* @param string $bin
|
||||
* The name of the wrapped cache bin.
|
||||
*/
|
||||
public function __construct(CacheDataCollector $cacheDataCollector, CacheBackendInterface $cacheBackend, $bin) {
|
||||
$this->cacheDataCollector = $cacheDataCollector;
|
||||
$this->cacheBackend = $cacheBackend;
|
||||
$this->bin = $bin;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get($cid, $allow_invalid = FALSE) {
|
||||
$cache = $this->cacheBackend->get($cid, $allow_invalid);
|
||||
|
||||
if ($cache) {
|
||||
$cacheCopy = new \StdClass();
|
||||
$cacheCopy->cid = $cache->cid;
|
||||
$cacheCopy->expire = $cache->expire;
|
||||
$cacheCopy->tags = $cache->tags;
|
||||
|
||||
$this->cacheDataCollector->registerCacheHit($this->bin, $cacheCopy);
|
||||
}
|
||||
else {
|
||||
$this->cacheDataCollector->registerCacheMiss($this->bin, $cid);
|
||||
}
|
||||
|
||||
return $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMultiple(&$cids, $allow_invalid = FALSE) {
|
||||
$cidsCopy = $cids;
|
||||
$cache = $this->cacheBackend->getMultiple($cids, $allow_invalid);
|
||||
|
||||
foreach ($cidsCopy as $cid) {
|
||||
if (in_array($cid, $cids)) {
|
||||
$this->cacheDataCollector->registerCacheMiss($this->bin, $cid);
|
||||
}
|
||||
else {
|
||||
$cacheCopy = new \StdClass();
|
||||
$cacheCopy->cid = $cache[$cid]->cid;
|
||||
$cacheCopy->expire = $cache[$cid]->expire;
|
||||
$cacheCopy->tags = $cache[$cid]->tags;
|
||||
|
||||
$this->cacheDataCollector->registerCacheHit($this->bin, $cacheCopy);
|
||||
}
|
||||
}
|
||||
|
||||
return $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = []) {
|
||||
return $this->cacheBackend->set($cid, $data, $expire, $tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setMultiple(array $items) {
|
||||
return $this->cacheBackend->setMultiple($items);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete($cid) {
|
||||
return $this->cacheBackend->delete($cid);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteMultiple(array $cids) {
|
||||
return $this->cacheBackend->deleteMultiple($cids);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteTags(array $tags) {
|
||||
return $this->cacheBackend->deleteTags($tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteAll() {
|
||||
return $this->cacheBackend->deleteAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function invalidate($cid) {
|
||||
return $this->cacheBackend->invalidate($cid);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function invalidateMultiple(array $cids) {
|
||||
return $this->cacheBackend->invalidateMultiple($cids);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function invalidateTags(array $tags) {
|
||||
return $this->cacheBackend->invalidateTags($tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function invalidateAll() {
|
||||
return $this->cacheBackend->invalidateAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function garbageCollection() {
|
||||
return $this->cacheBackend->garbageCollection();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeBin() {
|
||||
return $this->cacheBackend->removeBin();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Cache;
|
||||
|
||||
use Drupal\Core\Cache\CacheFactoryInterface;
|
||||
use Drupal\webprofiler\DataCollector\CacheDataCollector;
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
|
||||
|
||||
/**
|
||||
* Wraps a cache factory to register all calls to the cache system.
|
||||
*/
|
||||
class CacheFactoryWrapper implements CacheFactoryInterface, ContainerAwareInterface {
|
||||
|
||||
use ContainerAwareTrait;
|
||||
|
||||
/**
|
||||
* The cache factory.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\CacheFactoryInterface
|
||||
*/
|
||||
protected $cacheFactory;
|
||||
|
||||
/**
|
||||
* The cache data collector.
|
||||
*
|
||||
* @var \Drupal\webprofiler\DataCollector\CacheDataCollector
|
||||
*/
|
||||
protected $cacheDataCollector;
|
||||
|
||||
/**
|
||||
* All wrapped cache backends.
|
||||
*
|
||||
* @var \Drupal\webprofiler\Cache\CacheBackendWrapper[]
|
||||
*/
|
||||
protected $cacheBackends = [];
|
||||
|
||||
/**
|
||||
* Creates a new CacheFactoryWrapper instance.
|
||||
*
|
||||
* @param \Drupal\Core\Cache\CacheFactoryInterface $cache_factory
|
||||
* The cache factory.
|
||||
* @param \Drupal\webprofiler\DataCollector\CacheDataCollector $cacheDataCollector
|
||||
* The cache data collector.
|
||||
*/
|
||||
public function __construct(CacheFactoryInterface $cache_factory, CacheDataCollector $cacheDataCollector) {
|
||||
$this->cacheFactory = $cache_factory;
|
||||
$this->cacheDataCollector = $cacheDataCollector;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get($bin) {
|
||||
if (!isset($this->cacheBackends[$bin])) {
|
||||
$cache_backend = $this->cacheFactory->get($bin);
|
||||
$this->cacheBackends[$bin] = new CacheBackendWrapper($this->cacheDataCollector, $cache_backend, $bin);
|
||||
}
|
||||
return $this->cacheBackends[$bin];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Command;
|
||||
|
||||
use GuzzleHttp\ClientInterface;
|
||||
use GuzzleHttp\Cookie\CookieJar;
|
||||
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\Output\OutputInterface;
|
||||
use Drupal\Console\Command\ContainerAwareCommand;
|
||||
use Symfony\Component\DomCrawler\Crawler;
|
||||
use Symfony\Component\Process\Process;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
/**
|
||||
* Class BenchmarkCommand
|
||||
*/
|
||||
class BenchmarkCommand extends ContainerAwareCommand {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure() {
|
||||
$this
|
||||
->setName('webprofiler:benchmark')
|
||||
->setDescription($this->trans('commands.webprofiler.benchmark.description'))
|
||||
->addArgument('url', InputArgument::REQUIRED, $this->trans('commands.webprofiler.benchmark.arguments.url'))
|
||||
->addOption('runs', NULL, InputOption::VALUE_REQUIRED, $this->trans('commands.webprofiler.benchmark.options.runs'), 100)
|
||||
->addOption('file', NULL, InputOption::VALUE_REQUIRED, $this->trans('commands.webprofiler.benchmark.options.file'))
|
||||
->addOption('cache-rebuild', 'cr', InputOption::VALUE_NONE, $this->trans('commands.webprofiler.benchmark.options.cache_rebuild'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
$runs = $input->getOption('runs');
|
||||
$file = $input->getOption('file');
|
||||
$cache_rebuild = $input->getOption('cache-rebuild');
|
||||
|
||||
// http://username:password@hostname/
|
||||
$url = $input->getArgument('url');
|
||||
$url_components = parse_url($url);
|
||||
$login = isset($url_components['user']) && isset($url_components['pass']);
|
||||
|
||||
$steps = 3;
|
||||
|
||||
if ($cache_rebuild) {
|
||||
$steps++;
|
||||
}
|
||||
|
||||
if ($login) {
|
||||
$steps++;
|
||||
}
|
||||
|
||||
/** @var \Drupal\Core\Http\Client $client */
|
||||
$client = $this->getContainer()->get('http_client');
|
||||
|
||||
$progress = new ProgressBar($output, $runs + $steps);
|
||||
$progress->setFormat(' %current%/%max% [%bar%] %percent:3s%% %message%');
|
||||
|
||||
if ($cache_rebuild) {
|
||||
$progress->setMessage($this->trans('commands.webprofiler.benchmark.progress.cache_rebuild'));
|
||||
$this->RebuildCache();
|
||||
$progress->advance();
|
||||
}
|
||||
|
||||
if ($login) {
|
||||
$progress->setMessage($this->trans('commands.webprofiler.benchmark.progress.login'));
|
||||
$login_url = "{$url_components['scheme']}://{$url_components['host']}/user/login";
|
||||
|
||||
// Enable cookies storage.
|
||||
$cookieJar = new CookieJar();
|
||||
$client->setDefaultOption('cookies', $cookieJar);
|
||||
|
||||
// Retrieve a form_build_id using the DomCrawler component.
|
||||
$response = $client->get($login_url)->getBody()->getContents();
|
||||
$crawler = new Crawler($response);
|
||||
$form_build_id = $crawler->filter('#user-login-form input[name=form_build_id]')
|
||||
->attr('value');
|
||||
$op = $crawler->filter('#user-login-form input[name=op]')->attr('value');
|
||||
|
||||
// Login a user.
|
||||
$response = $client->post($login_url, [
|
||||
'body' => [
|
||||
'name' => $url_components['user'],
|
||||
'pass' => $url_components['pass'],
|
||||
'form_build_id' => $form_build_id,
|
||||
'form_id' => 'user_login_form',
|
||||
'op' => $op,
|
||||
]
|
||||
]);
|
||||
$progress->advance();
|
||||
|
||||
if ($response->getStatusCode() != 200) {
|
||||
throw new \Exception($this->trans('commands.webprofiler.benchmark.messages.error_login'));
|
||||
}
|
||||
}
|
||||
|
||||
$datas = [];
|
||||
for ($i = 0; $i < $runs; $i++) {
|
||||
$progress->setMessage($this->trans('commands.webprofiler.benchmark.progress.get'));
|
||||
$datas[] = $this->getData($client, $url);
|
||||
$progress->advance();
|
||||
}
|
||||
|
||||
$progress->setMessage($this->trans('commands.webprofiler.benchmark.progress.compute_avg'));
|
||||
$avg = $this->computeAvg($datas);
|
||||
$progress->advance();
|
||||
|
||||
$progress->setMessage($this->trans('commands.webprofiler.benchmark.progress.compute_median'));
|
||||
$median = $this->computePercentile($datas, 50);
|
||||
$progress->advance();
|
||||
|
||||
$progress->setMessage($this->trans('commands.webprofiler.benchmark.progress.compute_95percentile'));
|
||||
$percentile95 = $this->computePercentile($datas, 95);
|
||||
$progress->advance();
|
||||
|
||||
$progress->setMessage($this->trans('commands.webprofiler.benchmark.progress.git_hash'));
|
||||
$gitHash = $this->getGitHash();
|
||||
$progress->advance();
|
||||
|
||||
$progress->setMessage($this->trans('commands.webprofiler.benchmark.progress.yaml'));
|
||||
$yaml = $this->generateYaml($gitHash, $runs, $url, $avg, $median, $percentile95);
|
||||
$progress->advance();
|
||||
|
||||
$progress->setMessage($this->trans('commands.webprofiler.benchmark.progress.done'));
|
||||
$progress->finish();
|
||||
$output->writeln('');
|
||||
|
||||
if ($file) {
|
||||
file_put_contents($file, $yaml);
|
||||
}
|
||||
else {
|
||||
$output->writeln($yaml);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \GuzzleHttp\ClientInterface $client
|
||||
* @param $url
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getData(ClientInterface $client, $url) {
|
||||
/** @var \GuzzleHttp\Message\ResponseInterface $response */
|
||||
$response = $client->get($url);
|
||||
|
||||
$token = $response->getHeader('X-Debug-Token');
|
||||
|
||||
/** @var \Drupal\webprofiler\Profiler\Profiler $profiler */
|
||||
$profiler = $this->getContainer()->get('profiler');
|
||||
|
||||
/** @var \Symfony\Component\HttpKernel\Profiler\Profile $profile */
|
||||
$profile = $profiler->loadProfile($token);
|
||||
|
||||
/** @var \Drupal\webprofiler\DataCollector\TimeDataCollector $timeDataCollector */
|
||||
$timeDataCollector = $profile->getCollector('time');
|
||||
|
||||
return new BenchmarkData(
|
||||
$token,
|
||||
$timeDataCollector->getMemory(),
|
||||
$timeDataCollector->getDuration());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Drupal\webprofiler\Command\BenchmarkData[] $datas
|
||||
*
|
||||
* @return \Drupal\webprofiler\Command\BenchmarkData
|
||||
*/
|
||||
private function computeAvg($datas) {
|
||||
$profiles = count($datas);
|
||||
$totalTime = 0;
|
||||
$totalMemory = 0;
|
||||
|
||||
foreach ($datas as $data) {
|
||||
$totalTime += $data->getTime();
|
||||
$totalMemory += $data->getMemory();
|
||||
}
|
||||
|
||||
return new BenchmarkData(NULL, $totalMemory / $profiles, $totalTime / $profiles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes percentile using The Nearest Rank method.
|
||||
*
|
||||
* @param \Drupal\webprofiler\Command\BenchmarkData[] $datas
|
||||
* @param $percentile
|
||||
*
|
||||
* @return \Drupal\webprofiler\Command\BenchmarkData
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function computePercentile($datas, $percentile) {
|
||||
if ($percentile < 0 || $percentile > 100) {
|
||||
throw new \Exception('Percentile has to be between 0 and 100');
|
||||
}
|
||||
|
||||
$profiles = count($datas);
|
||||
|
||||
$n = ceil((($percentile / 100) * $profiles));
|
||||
$index = (int) $n - 1;
|
||||
|
||||
$orderedTime = $datas;
|
||||
$this->getOrderedDatas($orderedTime, 'Time');
|
||||
|
||||
$orderedMemory = $datas;
|
||||
$this->getOrderedDatas($orderedMemory, 'Memory');
|
||||
|
||||
return new BenchmarkData(NULL, $orderedMemory[$index]->getMemory(), $orderedTime[$index]->getTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function getGitHash() {
|
||||
try {
|
||||
$process = new Process('git rev-parse HEAD');
|
||||
$process->setTimeout(3600);
|
||||
$process->run();
|
||||
$git_hash = $process->getOutput();
|
||||
} catch (\Exception $e) {
|
||||
$git_hash = $this->trans('commands.webprofiler.benchmark.messages.not_git');
|
||||
}
|
||||
|
||||
return $git_hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Drupal\webprofiler\Command\BenchmarkData[] $datas
|
||||
* @param $string
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getOrderedDatas(&$datas, $string) {
|
||||
usort($datas, function ($a, $b) use ($string) {
|
||||
$method = 'get' . $string;
|
||||
if ($a->{$method} > $b->{$method}) {
|
||||
return 1;
|
||||
}
|
||||
if ($a->{$method} < $b->{$method}) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuilds Drupal cache.
|
||||
*/
|
||||
protected function RebuildCache() {
|
||||
require_once DRUPAL_ROOT . '/core/includes/utility.inc';
|
||||
$kernelHelper = $this->getHelper('kernel');
|
||||
$classLoader = $kernelHelper->getClassLoader();
|
||||
$request = $kernelHelper->getRequest();
|
||||
drupal_rebuild($classLoader, $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $gitHash
|
||||
* @param $runs
|
||||
* @param $url
|
||||
* @param \Drupal\webprofiler\Command\BenchmarkData $avg
|
||||
* @param \Drupal\webprofiler\Command\BenchmarkData $median
|
||||
* @param \Drupal\webprofiler\Command\BenchmarkData $percentile95
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function generateYaml($gitHash, $runs, $url, BenchmarkData $avg, BenchmarkData $median, BenchmarkData $percentile95) {
|
||||
$yaml = Yaml::dump([
|
||||
'date' => date($this->trans('commands.webprofiler.list.rows.time'), time()),
|
||||
'git_commit' => $gitHash,
|
||||
'number_of_runs' => $runs,
|
||||
'url' => $url,
|
||||
'results' => [
|
||||
'average' => [
|
||||
'time' => sprintf('%.0f ms', $avg->getTime()),
|
||||
'memory' => sprintf('%.1f MB', $avg->getMemory() / 1024 / 1024),
|
||||
],
|
||||
'median' => [
|
||||
'time' => sprintf('%.0f ms', $median->getTime()),
|
||||
'memory' => sprintf('%.1f MB', $median->getMemory() / 1024 / 1024),
|
||||
],
|
||||
'95_percentile' => [
|
||||
'time' => sprintf('%.0f ms', $percentile95->getTime()),
|
||||
'memory' => sprintf('%.1f MB', $percentile95->getMemory() / 1024 / 1024),
|
||||
],
|
||||
],
|
||||
]);
|
||||
return $yaml;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function showMessage($output, $message, $type = 'info') {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Command;
|
||||
|
||||
/**
|
||||
* Class BenchmarkData
|
||||
*/
|
||||
class BenchmarkData {
|
||||
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
private $token;
|
||||
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
private $memory;
|
||||
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
private $time;
|
||||
|
||||
/**
|
||||
* @param $token
|
||||
* @param $memory
|
||||
* @param $time
|
||||
*/
|
||||
public function __construct($token, $memory, $time) {
|
||||
$this->token = $token;
|
||||
$this->memory = $memory;
|
||||
$this->time = $time;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getToken() {
|
||||
return $this->token;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getMemory() {
|
||||
return $this->memory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getTime() {
|
||||
return $this->time;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Command;
|
||||
|
||||
use Drupal\Console\Command\ContainerAwareCommand;
|
||||
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\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Class ExportCommand
|
||||
*/
|
||||
class ExportCommand extends ContainerAwareCommand {
|
||||
|
||||
/** @var string */
|
||||
private $filename;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure() {
|
||||
$this
|
||||
->setName('webprofiler:export')
|
||||
->setDescription($this->trans('commands.webprofiler.export.description'))
|
||||
->addArgument('id', InputArgument::OPTIONAL, $this->trans('commands.webprofiler.export.arguments.id'))
|
||||
->addOption('directory', NULL, InputOption::VALUE_REQUIRED, $this->trans('commands.webprofiler.export.options.directory'), '/tmp');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
$id = $input->getArgument('id');
|
||||
$directory = $input->getOption('directory');
|
||||
|
||||
/** @var \Drupal\webprofiler\Profiler\Profiler $profiler */
|
||||
$profiler = $this->getContainer()->get('profiler');
|
||||
|
||||
try {
|
||||
if ($id) {
|
||||
$this->filename = $this->exportSingle($profiler, $id, $directory);
|
||||
}
|
||||
else {
|
||||
$this->filename = $this->exportAll($profiler, $directory, $output);
|
||||
}
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$output->writeln('<error>' . $e->getMessage() . '</error>');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports a single profile.
|
||||
*
|
||||
* @param \Drupal\webprofiler\Profiler\Profiler $profiler
|
||||
* @param int $id
|
||||
* @param string $directory
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function exportSingle(Profiler $profiler, $id, $directory) {
|
||||
$profile = $profiler->loadProfile($id);
|
||||
if ($profile) {
|
||||
$data = $profiler->export($profile);
|
||||
|
||||
$filename = $directory . DIRECTORY_SEPARATOR . $id . '.txt';
|
||||
if (file_put_contents($filename, $data) === FALSE) {
|
||||
throw new \Exception(sprintf(
|
||||
$this->trans('commands.webprofiler.export.messages.error_writing'),
|
||||
$filename));
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new \Exception(sprintf(
|
||||
$this->trans('commands.webprofiler.export.messages.error_no_profile'),
|
||||
$id));
|
||||
}
|
||||
|
||||
return $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports all stored profiles (cap limit at 1000 items).
|
||||
*
|
||||
* @param \Drupal\webprofiler\Profiler\Profiler $profiler
|
||||
* @param string $directory
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function exportAll(Profiler $profiler, $directory, $output) {
|
||||
$filename = $directory . DIRECTORY_SEPARATOR . 'profiles_' . time() . '.tar.gz';
|
||||
$archiver = new ArchiveTar($filename, 'gz');
|
||||
$profiles = $profiler->find(NULL, NULL, 1000, NULL, '', '');
|
||||
$progress = new ProgressBar($output, count($profiles) + 2);
|
||||
$progress->setFormat(' %current%/%max% [%bar%] %percent:3s%% %message%');
|
||||
|
||||
$files = [];
|
||||
$progress->start();
|
||||
$progress->setMessage($this->trans('commands.webprofiler.export.progress.exporting'));
|
||||
foreach ($profiles as $profile) {
|
||||
$data = $profiler->export($profiler->loadProfile($profile['token']));
|
||||
$profileFilename = $directory . "/{$profile['token']}.txt";
|
||||
file_put_contents($profileFilename, $data);
|
||||
$files[] = $profileFilename;
|
||||
$progress->advance();
|
||||
}
|
||||
|
||||
$progress->setMessage($this->trans('commands.webprofiler.export.progress.archive'));
|
||||
$archiver->createModify($files, '', $directory);
|
||||
$progress->advance();
|
||||
|
||||
$progress->setMessage($this->trans('commands.webprofiler.export.progress.delete_tmp'));
|
||||
foreach ($files as $file) {
|
||||
unlink($file);
|
||||
}
|
||||
$progress->advance();
|
||||
|
||||
$progress->setMessage($this->trans('commands.webprofiler.export.progress.done'));
|
||||
$progress->finish();
|
||||
$output->writeln('');
|
||||
|
||||
$output->writeln(sprintf(
|
||||
$this->trans('commands.webprofiler.export.messages.exported_count'),
|
||||
count($profiles)));
|
||||
|
||||
return $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function showMessage($output, $message, $type = 'info') {
|
||||
if (!$this->filename) {
|
||||
return;
|
||||
}
|
||||
|
||||
$completeMessageKey = 'commands.webprofiler.export.messages.success';
|
||||
$completeMessage = sprintf($this->trans($completeMessageKey), $this->filename);
|
||||
|
||||
if ($completeMessage != $completeMessageKey) {
|
||||
parent::showMessage($output, $completeMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Command;
|
||||
|
||||
use Drupal\Console\Command\ContainerAwareCommand;
|
||||
use Symfony\Component\Console\Helper\Table;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Class ListCommand
|
||||
*/
|
||||
class ListCommand extends ContainerAwareCommand {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure() {
|
||||
$this
|
||||
->setName('webprofiler:list')
|
||||
->setDescription($this->trans('commands.webprofiler.list.description'))
|
||||
->addOption('ip', NULL, InputOption::VALUE_REQUIRED, $this->trans('commands.webprofiler.list.options.ip'), NULL)
|
||||
->addOption('url', NULL, InputOption::VALUE_REQUIRED, $this->trans('commands.webprofiler.list.options.url'), NULL)
|
||||
->addOption('method', NULL, InputOption::VALUE_REQUIRED, $this->trans('commands.webprofiler.list.options.method'), NULL)
|
||||
->addOption('limit', NULL, InputOption::VALUE_REQUIRED, $this->trans('commands.webprofiler.list.options.limit'), 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
$ip = $input->getOption('ip');
|
||||
$url = $input->getOption('url');
|
||||
$method = $input->getOption('method');
|
||||
$limit = $input->getOption('limit');
|
||||
|
||||
/** @var \Drupal\webprofiler\Profiler\Profiler $profiler */
|
||||
$profiler = $this->getContainer()->get('profiler');
|
||||
$profiles = $profiler->find($ip, $url, $limit, $method, '', '');
|
||||
|
||||
$rows = [];
|
||||
foreach ($profiles as $profile) {
|
||||
$row = [];
|
||||
|
||||
$row[] = $profile['token'];
|
||||
$row[] = $profile['ip'];
|
||||
$row[] = $profile['method'];
|
||||
$row[] = $profile['url'];
|
||||
$row[] = date($this->trans('commands.webprofiler.list.rows.time'), $profile['time']);
|
||||
|
||||
$rows[] = $row;
|
||||
}
|
||||
|
||||
$table = new Table($output);
|
||||
$table
|
||||
->setHeaders([
|
||||
$this->trans('commands.webprofiler.list.header.token'),
|
||||
$this->trans('commands.webprofiler.list.header.ip'),
|
||||
$this->trans('commands.webprofiler.list.header.method'),
|
||||
$this->trans('commands.webprofiler.list.header.url'),
|
||||
$this->trans('commands.webprofiler.list.header.time'),
|
||||
])
|
||||
->setRows($rows);
|
||||
$table->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function showMessage($output, $message, $type = 'info') {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
* Class DecoratorPass.
|
||||
*/
|
||||
class DecoratorPass implements CompilerPassInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function process(ContainerBuilder $container) {
|
||||
// Builds a decorator around plugin.manager.mail.
|
||||
$definition = $container->findDefinition('plugin.manager.mail');
|
||||
$definition->setPublic(FALSE);
|
||||
$container->setDefinition('webprofiler.debug.plugin.manager.mail.default', $definition);
|
||||
$container->register('plugin.manager.mail', 'Drupal\webprofiler\Mail\MailManagerWrapper')
|
||||
->addArgument(new Reference('container.namespaces'))
|
||||
->addArgument(new Reference('cache.discovery'))
|
||||
->addArgument(new Reference('module_handler'))
|
||||
->addArgument(new Reference('config.factory'))
|
||||
->addArgument(new Reference('logger.factory'))
|
||||
->addArgument(new Reference('string_translation'))
|
||||
->addArgument(new Reference('webprofiler.debug.plugin.manager.mail.default'))
|
||||
->addArgument(new Reference('webprofiler.mail'))
|
||||
->setProperty('_serviceId', 'plugin.manager.mail');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?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');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Compiler;
|
||||
|
||||
use Drupal\Core\StreamWrapper\PublicStream;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
|
||||
/**
|
||||
* Class ProfilerPass
|
||||
*/
|
||||
class ProfilerPass implements CompilerPassInterface {
|
||||
|
||||
/**
|
||||
* @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function process(ContainerBuilder $container) {
|
||||
// configure the profiler service
|
||||
if (FALSE === $container->hasDefinition('profiler')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$definition = $container->getDefinition('profiler');
|
||||
|
||||
$collectors = new \SplPriorityQueue();
|
||||
$order = PHP_INT_MAX;
|
||||
foreach ($container->findTaggedServiceIds('data_collector') as $id => $attributes) {
|
||||
$priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0;
|
||||
$template = NULL;
|
||||
|
||||
if (isset($attributes[0]['template'])) {
|
||||
if (!isset($attributes[0]['id'])) {
|
||||
throw new \InvalidArgumentException(sprintf('Data collector service "%s" must have an id attribute in order to specify a template', $id));
|
||||
}
|
||||
if (!isset($attributes[0]['title'])) {
|
||||
throw new \InvalidArgumentException(sprintf('Data collector service "%s" must have a title attribute', $id));
|
||||
}
|
||||
|
||||
$template = [
|
||||
$attributes[0]['id'],
|
||||
$attributes[0]['template'],
|
||||
$attributes[0]['title']
|
||||
];
|
||||
}
|
||||
|
||||
$collectors->insert([$id, $template], [-$priority, --$order]);
|
||||
}
|
||||
|
||||
$templates = [];
|
||||
foreach ($collectors as $collector) {
|
||||
$definition->addMethodCall('add', [new Reference($collector[0])]);
|
||||
$templates[$collector[0]] = $collector[1];
|
||||
}
|
||||
|
||||
$container->setParameter('data_collector.templates', $templates);
|
||||
|
||||
// set parameter to store the public folder path
|
||||
$path = 'file:' . DRUPAL_ROOT . '/' . PublicStream::basePath() . '/profiler';
|
||||
$container->setParameter('data_collector.storage', $path);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\Compiler\ServiceReferenceGraph;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
|
||||
/**
|
||||
* Class ServicePass
|
||||
*/
|
||||
class ServicePass implements CompilerPassInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function process(ContainerBuilder $container) {
|
||||
if (FALSE === $container->hasDefinition('webprofiler.services')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$definition = $container->getDefinition('webprofiler.services');
|
||||
$graph = $container->getCompiler()->getServiceReferenceGraph();
|
||||
|
||||
$definition->addMethodCall('setServices', [$this->extractData($container, $graph)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
|
||||
* @param \Symfony\Component\DependencyInjection\Compiler\ServiceReferenceGraph $graph
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function extractData(ContainerBuilder $container, ServiceReferenceGraph $graph) {
|
||||
$data = [];
|
||||
|
||||
foreach ($container->getDefinitions() as $id => $definition) {
|
||||
$inEdges = [];
|
||||
$outEdges = [];
|
||||
|
||||
if ($graph->hasNode($id)) {
|
||||
$node = $graph->getNode($id);
|
||||
|
||||
/** @var \Symfony\Component\DependencyInjection\Compiler\ServiceReferenceGraphEdge $edge */
|
||||
foreach ($node->getInEdges() as $edge) {
|
||||
/** @var \Symfony\Component\DependencyInjection\Reference $edgeValue */
|
||||
$edgeValue = $edge->getValue();
|
||||
|
||||
$inEdges[] = [
|
||||
'id' => $edge->getSourceNode()->getId(),
|
||||
'invalidBehavior' => $edgeValue ? $edgeValue->getInvalidBehavior() : NULL,
|
||||
'strict' => $edgeValue ? $edgeValue->isStrict() : NULL,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/** @var \Symfony\Component\DependencyInjection\Compiler\ServiceReferenceGraphEdge $edge */
|
||||
foreach ($node->getOutEdges() as $edge) {
|
||||
/** @var \Symfony\Component\DependencyInjection\Reference $edgeValue */
|
||||
$edgeValue = $edge->getValue();
|
||||
|
||||
$outEdges[] = [
|
||||
'id' => $edge->getDestNode()->getId(),
|
||||
'invalidBehavior' => $edgeValue ? $edgeValue->getInvalidBehavior() : NULL,
|
||||
'strict' => $edgeValue ? $edgeValue->isStrict() : NULL,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if ($definition instanceof Definition) {
|
||||
$class = $definition->getClass();
|
||||
|
||||
try {
|
||||
$reflectedClass = new \ReflectionClass($class);
|
||||
$file = $reflectedClass->getFileName();
|
||||
} catch (\ReflectionException $e) {
|
||||
$file = NULL;
|
||||
}
|
||||
|
||||
$tags = $definition->getTags();
|
||||
$public = $definition->isPublic();
|
||||
$synthetic = $definition->isSynthetic();
|
||||
}
|
||||
else {
|
||||
$id = $definition->__toString();
|
||||
$class = NULL;
|
||||
$file = NULL;
|
||||
$tags = [];
|
||||
$public = NULL;
|
||||
$synthetic = NULL;
|
||||
}
|
||||
|
||||
$data[$id] = [
|
||||
'inEdges' => $inEdges,
|
||||
'outEdges' => $outEdges,
|
||||
'value' => [
|
||||
'class' => $class,
|
||||
'file' => $file,
|
||||
'id' => $id,
|
||||
'tags' => $tags,
|
||||
'public' => $public,
|
||||
'synthetic' => $synthetic,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
|
||||
/**
|
||||
* Class StoragePass
|
||||
*/
|
||||
class StoragePass implements CompilerPassInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function process(ContainerBuilder $container) {
|
||||
if (FALSE === $container->hasDefinition('profiler.storage_manager')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$definition = $container->getDefinition('profiler.storage_manager');
|
||||
|
||||
foreach ($container->findTaggedServiceIds('webprofiler_storage') as $id => $attributes) {
|
||||
$definition->addMethodCall('addStorage', [
|
||||
$id,
|
||||
$attributes[0]['title'],
|
||||
new Reference($id)
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Config;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactory;
|
||||
use Drupal\webprofiler\DataCollector\ConfigDataCollector;
|
||||
|
||||
/**
|
||||
* Wraps a config factory to be able to figure out all used config files.
|
||||
*/
|
||||
class ConfigFactoryWrapper extends ConfigFactory {
|
||||
|
||||
/**
|
||||
* @var \Drupal\webprofiler\DataCollector\ConfigDataCollector
|
||||
*/
|
||||
private $dataCollector;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get($name) {
|
||||
$result = parent::get($name);
|
||||
$this->dataCollector->addConfigName($name);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadMultiple(array $names) {
|
||||
$result = parent::loadMultiple($names);
|
||||
foreach (array_keys($result) as $name) {
|
||||
$this->dataCollector->addConfigName($name);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Drupal\webprofiler\DataCollector\ConfigDataCollector $dataCollector
|
||||
*/
|
||||
public function setDataCollector(ConfigDataCollector $dataCollector) {
|
||||
$this->dataCollector = $dataCollector;
|
||||
}
|
||||
}
|
||||
+243
@@ -0,0 +1,243 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Controller;
|
||||
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Datetime\DateFormatter;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\webprofiler\DrupalDataCollectorInterface;
|
||||
use Drupal\webprofiler\Profiler\ProfilerStorageManager;
|
||||
use Drupal\webprofiler\Profiler\TemplateManager;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Drupal\webprofiler\Profiler\Profiler;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpKernel\Profiler\Profile;
|
||||
use Symfony\Component\Routing\RouterInterface;
|
||||
|
||||
/**
|
||||
* Class DashboardController
|
||||
*/
|
||||
class DashboardController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* @var \Drupal\webprofiler\Profiler\Profiler
|
||||
*/
|
||||
private $profiler;
|
||||
|
||||
/**
|
||||
* @var \Symfony\Cmf\Component\Routing\ChainRouter
|
||||
*/
|
||||
private $router;
|
||||
|
||||
/**
|
||||
* @var \Drupal\webprofiler\Profiler\TemplateManager
|
||||
*/
|
||||
private $templateManager;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Datetime\DateFormatter
|
||||
*/
|
||||
private $date;
|
||||
|
||||
/**
|
||||
* @var \Drupal\webprofiler\Profiler\ProfilerStorageManager
|
||||
*/
|
||||
private $storageManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('profiler'),
|
||||
$container->get('router'),
|
||||
$container->get('template_manager'),
|
||||
$container->get('date.formatter'),
|
||||
$container->get('profiler.storage_manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new WebprofilerController.
|
||||
*
|
||||
* @param \Drupal\webprofiler\Profiler\Profiler $profiler
|
||||
* @param \Symfony\Component\Routing\RouterInterface $router
|
||||
* @param \Drupal\webprofiler\Profiler\TemplateManager $templateManager
|
||||
* @param \Drupal\Core\Datetime\DateFormatter $date
|
||||
* @param \Drupal\webprofiler\Profiler\ProfilerStorageManager $storageManager
|
||||
*/
|
||||
public function __construct(Profiler $profiler, RouterInterface $router, TemplateManager $templateManager, DateFormatter $date, ProfilerStorageManager $storageManager) {
|
||||
$this->profiler = $profiler;
|
||||
$this->router = $router;
|
||||
$this->templateManager = $templateManager;
|
||||
$this->date = $date;
|
||||
$this->storageManager = $storageManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the dashboard page.
|
||||
*
|
||||
* @param Profile $profile
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function dashboardAction(Profile $profile) {
|
||||
$this->profiler->disable();
|
||||
|
||||
$templateManager = $this->templateManager;
|
||||
$templates = $templateManager->getTemplates($profile);
|
||||
|
||||
$panels = [];
|
||||
$libraries = ['webprofiler/dashboard'];
|
||||
$drupalSettings = [
|
||||
'webprofiler' => [
|
||||
'token' => $profile->getToken(),
|
||||
'ide_link' => $this->config('webprofiler.config')->get('ide_link'),
|
||||
'ide_link_remote' => $this->config('webprofiler.config')->get('ide_link_remote'),
|
||||
'ide_link_local' => $this->config('webprofiler.config')->get('ide_link_local'),
|
||||
'collectors' => [],
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($templates as $name => $template) {
|
||||
/** @var DrupalDataCollectorInterface $collector */
|
||||
$collector = $profile->getCollector($name);
|
||||
|
||||
if ($collector->hasPanel()) {
|
||||
$rendered = $template->renderBlock('panel', [
|
||||
'token' => $profile->getToken(),
|
||||
'name' => $name,
|
||||
]);
|
||||
|
||||
$panels[] = [
|
||||
'#theme' => 'webprofiler_panel',
|
||||
'#panel' => $rendered,
|
||||
];
|
||||
|
||||
$drupalSettings['webprofiler']['collectors'][] = [
|
||||
'id' => $name,
|
||||
'name' => $name,
|
||||
'label' => $collector->getTitle(),
|
||||
'summary' => $collector->getPanelSummary(),
|
||||
'icon' => $collector->getIcon(),
|
||||
];
|
||||
|
||||
$libraries = array_merge($libraries, $collector->getLibraries());
|
||||
$drupalSettings['webprofiler'] += $collector->getDrupalSettings();
|
||||
}
|
||||
}
|
||||
|
||||
$build = [];
|
||||
$build['panels'] = [
|
||||
'#theme' => 'webprofiler_dashboard',
|
||||
'#profile' => $profile,
|
||||
'#panels' => $panels,
|
||||
'#spinner_path' => '/' . $this->moduleHandler()
|
||||
->getModule('webprofiler')
|
||||
->getPath() . '/images/searching.gif',
|
||||
'#attached' => [
|
||||
'drupalSettings' => $drupalSettings,
|
||||
'library' => $libraries,
|
||||
],
|
||||
];
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the list page.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function listAction(Request $request) {
|
||||
$limit = $request->get('limit', 10);
|
||||
$this->profiler->disable();
|
||||
|
||||
$ip = $request->query->get('ip');
|
||||
$method = $request->query->get('method');
|
||||
$url = $request->query->get('url');
|
||||
|
||||
$profiles = $this->profiler->find($ip, $url, $limit, $method, '', '');
|
||||
|
||||
$rows = [];
|
||||
if (count($profiles)) {
|
||||
foreach ($profiles as $profile) {
|
||||
$row = [];
|
||||
$row[] = $this->l($profile['token'], new Url('webprofiler.dashboard', ['profile' => $profile['token']]));
|
||||
$row[] = $profile['ip'];
|
||||
$row[] = $profile['method'];
|
||||
$row[] = $profile['url'];
|
||||
$row[] = $this->date->format($profile['time']);
|
||||
|
||||
$rows[] = $row;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$rows[] = [
|
||||
[
|
||||
'data' => $this->t('No profiles found'),
|
||||
'colspan' => 6,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$build = [];
|
||||
|
||||
$storage_id = $this->config('webprofiler.config')->get('storage');
|
||||
$storage = $this->storageManager->getStorage($storage_id);
|
||||
|
||||
$build['resume'] = [
|
||||
'#type' => 'inline_template',
|
||||
'#template' => '<p>{{ message }}</p>',
|
||||
'#context' => [
|
||||
'message' => $this->t('Profiles stored with %storage service.', ['%storage' => $storage['title']]),
|
||||
],
|
||||
];
|
||||
|
||||
$build['filters'] = $this->formBuilder()
|
||||
->getForm('Drupal\\webprofiler\\Form\\ProfilesFilterForm');
|
||||
|
||||
$build['table'] = [
|
||||
'#type' => 'table',
|
||||
'#rows' => $rows,
|
||||
'#header' => [
|
||||
$this->t('Token'),
|
||||
[
|
||||
'data' => $this->t('Ip'),
|
||||
'class' => [RESPONSIVE_PRIORITY_LOW],
|
||||
],
|
||||
[
|
||||
'data' => $this->t('Method'),
|
||||
'class' => [RESPONSIVE_PRIORITY_LOW],
|
||||
],
|
||||
$this->t('Url'),
|
||||
[
|
||||
'data' => $this->t('Time'),
|
||||
'class' => [RESPONSIVE_PRIORITY_MEDIUM],
|
||||
],
|
||||
],
|
||||
'#sticky' => TRUE,
|
||||
];
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exposes collector's data as JSON.
|
||||
*
|
||||
* @param \Symfony\Component\HttpKernel\Profiler\Profile $profile
|
||||
* @param $collector
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\JsonResponse
|
||||
*/
|
||||
public function restCollectorAction(Profile $profile, $collector) {
|
||||
$this->profiler->disable();
|
||||
|
||||
$data = $profile->getCollector($collector)->getData();
|
||||
|
||||
return new JsonResponse(['data' => $data]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Controller;
|
||||
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\webprofiler\DataCollector\DatabaseDataCollector;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Symfony\Component\HttpKernel\Profiler\Profile;
|
||||
use Symfony\Component\HttpKernel\Profiler\Profiler;
|
||||
|
||||
/**
|
||||
* Class DatabaseController
|
||||
*/
|
||||
class DatabaseController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* @var \Symfony\Component\HttpKernel\Profiler\Profiler
|
||||
*/
|
||||
private $profiler;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
private $database;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('profiler'),
|
||||
$container->get('database')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new WebprofilerController.
|
||||
*
|
||||
* @param \Symfony\Component\HttpKernel\Profiler\Profiler $profiler
|
||||
* @param \Drupal\Core\Database\Connection $database
|
||||
*/
|
||||
public function __construct(Profiler $profiler, Connection $database) {
|
||||
$this->profiler = $profiler;
|
||||
$this->database = $database;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Profile $profile
|
||||
* @param int $qid
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function explainAction(Profile $profile, $qid) {
|
||||
$query = $this->getQuery($profile, $qid);
|
||||
|
||||
$data = [];
|
||||
$result = $this->database->query('EXPLAIN ' . $query['query'], (array) $query['args'])
|
||||
->fetchAllAssoc('table');
|
||||
$i = 1;
|
||||
foreach ($result as $row) {
|
||||
foreach ($row as $key => $value) {
|
||||
$data[$i][$key] = $value;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
|
||||
return new JsonResponse(['data' => $data]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $profile ->getToken()
|
||||
* @param int $qid
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getQuery(Profile $profile, $qid) {
|
||||
$this->profiler->disable();
|
||||
$token = $profile->getToken();
|
||||
|
||||
if (!$profile = $this->profiler->loadProfile($token)) {
|
||||
throw new NotFoundHttpException($this->t('Token @token does not exist.', ['@token' => $token]));
|
||||
}
|
||||
|
||||
/** @var DatabaseDataCollector $databaseCollector */
|
||||
$databaseCollector = $profile->getCollector('database');
|
||||
|
||||
$queries = $databaseCollector->getQueries();
|
||||
$query = $queries[$qid];
|
||||
|
||||
return $query;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Controller;
|
||||
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Render\RendererInterface;
|
||||
use Drupal\webprofiler\Profiler\Profiler;
|
||||
use Drupal\webprofiler\Profiler\TemplateManager;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Profiler\Profile;
|
||||
|
||||
/**
|
||||
* Class ToolbarController
|
||||
*/
|
||||
class ToolbarController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* @var \Drupal\webprofiler\Profiler\Profiler
|
||||
*/
|
||||
private $profiler;
|
||||
|
||||
/**
|
||||
* @var \Drupal\webprofiler\Profiler\TemplateManager
|
||||
*/
|
||||
private $templateManager;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Render\RendererInterface
|
||||
*/
|
||||
private $renderer;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('profiler'),
|
||||
$container->get('template_manager'),
|
||||
$container->get('renderer')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new WebprofilerController.
|
||||
*
|
||||
* @param \Drupal\webprofiler\Profiler\Profiler $profiler
|
||||
* @param \Drupal\webprofiler\Profiler\TemplateManager $templateManager
|
||||
* @param \Drupal\Core\Render\RendererInterface $renderer
|
||||
*/
|
||||
public function __construct(Profiler $profiler, TemplateManager $templateManager, RendererInterface $renderer) {
|
||||
$this->profiler = $profiler;
|
||||
$this->templateManager = $templateManager;
|
||||
$this->renderer = $renderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the toolbar.
|
||||
*
|
||||
* @param Profile $profile
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toolbarAction(Profile $profile) {
|
||||
$this->profiler->disable();
|
||||
|
||||
$templates = $this->templateManager->getTemplates($profile);
|
||||
|
||||
$rendered = '';
|
||||
foreach ($templates as $name => $template) {
|
||||
$rendered .= $template->renderBlock('toolbar', [
|
||||
'collector' => $profile->getcollector($name),
|
||||
'token' => $profile->getToken(),
|
||||
'name' => $name
|
||||
]);
|
||||
}
|
||||
|
||||
$toolbar = [
|
||||
'#theme' => 'webprofiler_toolbar',
|
||||
'#toolbar' => $rendered,
|
||||
'#token' => $profile->getToken(),
|
||||
];
|
||||
|
||||
return new Response($this->renderer->render($toolbar));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Symfony\Component\HttpKernel\Profiler\Profile $profile
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\JsonResponse
|
||||
*/
|
||||
public function savePerformanceTimingAction(Profile $profile, Request $request) {
|
||||
$this->profiler->disable();
|
||||
|
||||
$data = Json::decode($request->getContent());
|
||||
|
||||
/** @var $collector */
|
||||
$collector = $profile->getCollector('performance_timing');
|
||||
$collector->setData($data);
|
||||
$this->profiler->updateProfile($profile);
|
||||
|
||||
return new JsonResponse(['success' => TRUE]);
|
||||
}
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\DataCollector;
|
||||
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\webprofiler\DrupalDataCollectorInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
||||
|
||||
/**
|
||||
* Collects data about the used assets (CSS/JS).
|
||||
*/
|
||||
class AssetsDataCollector extends DataCollector implements DrupalDataCollectorInterface {
|
||||
|
||||
use StringTranslationTrait, DrupalDataCollectorTrait;
|
||||
|
||||
/**
|
||||
* The app root.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $root;
|
||||
|
||||
/**
|
||||
* Constructs a AssetDataCollector object.
|
||||
*
|
||||
* @param string $root
|
||||
* The app root.
|
||||
*/
|
||||
public function __construct($root) {
|
||||
$this->root = $root;
|
||||
|
||||
$this->data['js'] = [];
|
||||
$this->data['css'] = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Exception $exception = NULL) {
|
||||
$this->data['assets']['installation_path'] = $this->root . '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $jsAsset
|
||||
*/
|
||||
public function addJsAsset($jsAsset) {
|
||||
$this->data['js'] = NestedArray::mergeDeepArray([
|
||||
$jsAsset,
|
||||
$this->data['js']
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $cssAsset
|
||||
*/
|
||||
public function addCssAsset($cssAsset) {
|
||||
$this->data['css'] = NestedArray::mergeDeepArray([
|
||||
$cssAsset,
|
||||
$this->data['css']
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Twig callback to return the amount of CSS files.
|
||||
*/
|
||||
public function getCssCount() {
|
||||
return count($this->data['css']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Twig callback to return the amount of JS files.
|
||||
*/
|
||||
public function getJsCount() {
|
||||
return count($this->data['js']) - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName() {
|
||||
return 'assets';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle() {
|
||||
return $this->t('Assets');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPanelSummary() {
|
||||
return $this->t('Total: @count', ['@count' => ($this->getCssCount() + $this->getJsCount())]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIcon() {
|
||||
return 'iVBORw0KGgoAAAANSUhEUgAAABUAAAAcCAYAAACOGPReAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNS4xIE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxQUE0NEI2NTlCQTkxMUUzQkFDRjg2NUVCQ0NFNTcwQiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoxQUE0NEI2NjlCQTkxMUUzQkFDRjg2NUVCQ0NFNTcwQiI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjFBQTQ0QjYzOUJBOTExRTNCQUNGODY1RUJDQ0U1NzBCIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjFBQTQ0QjY0OUJBOTExRTNCQUNGODY1RUJDQ0U1NzBCIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+C1mVdgAAAktJREFUeNrUlk+I6VEUx48/4SVKpkgiCywkK8ksWVhYSFm9hYXFqxfbt2KnLJS9hbKQhZXtFMVmUrKYkZmslPxLSvnvYZx37+2NTDPP03ss5tTpcu/xued77rm/Hw4iwqWNC1cwzv8CPlJ6lUypfSf+k256Ad8R/0HlL4l/uWCSL5zfO/xTLTkczrvx9aDwU7QUX61Ww/Pz88WAdrv9Oplyzz0UPp8PIpEIzrnWJ6EUJBAI2DgYDOD+/h7EYjFwuadz4X80SUFCoRB6vR6Mx2Mwm82QTqfh4eEBNBoN3NzcgFQqhdVqBbvd7s+ZUlkUJpFIoN1uQyQSAYvFAqlUiq2Xy2VwuVwQCoXAZrNBMpmE6XTK4nk83lsqOX0ki0h2xUajgYFAAJVKJRoMBoxGo9hqtXA4HCKNq1ar+Pj4iMFgEFUqFer1egyHw9jv93GxWDCOyWTCA5RaLBZjd9jhcGCz2cRXKxQKqNVqcTQaHeZKpRLqdDoWf3d3h6QMB+hB/nK5BL/fD4lEAsiPmUSfzwf1eh1qtRoYjUaQy+WQz+eBbAoej4fVN5PJgNVqZfV9J586lUBtMplgLpdDEoyktuj1ejEej7M1p9OJbrcbi8UirtdrNjebzQ6MN/KPfT6f436/Z3XudDp4e3uLlUqFQej37XbL5B7DjqEfthQBAgGzfpTJZJDNZpn0zWbDRgI/eQlOdjGFU1coFIfHGu3Lv90qbrfbXRFpJ4POAVGjh/r09PRCP38l3r3Q62RA/NtV3qb8T/Nn4irQXwIMANMNuV/Q8qbhAAAAAElFTkSuQmCC';
|
||||
}
|
||||
}
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\DataCollector;
|
||||
|
||||
use Drupal\block\Entity\Block;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\webprofiler\DrupalDataCollectorInterface;
|
||||
use Drupal\webprofiler\Entity\EntityDecorator;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
||||
|
||||
/**
|
||||
* Class BlocksDataCollector
|
||||
*/
|
||||
class BlocksDataCollector extends DataCollector implements DrupalDataCollectorInterface {
|
||||
|
||||
use StringTranslationTrait, DrupalDataCollectorTrait;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
private $entityManager;
|
||||
|
||||
/**
|
||||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityManager
|
||||
*/
|
||||
public function __construct(EntityTypeManagerInterface $entityManager) {
|
||||
$this->entityManager = $entityManager;
|
||||
|
||||
$this->data['blocks']['loaded'] = [];
|
||||
$this->data['blocks']['rendered'] = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Exception $exception = NULL) {
|
||||
$storage = $this->entityManager->getStorage('block');
|
||||
|
||||
$loaded = $this->entityManager->getLoaded('config', 'block');
|
||||
$rendered = $this->entityManager->getRendered('block');
|
||||
|
||||
if ($loaded) {
|
||||
$this->data['blocks']['loaded'] = $this->getBlocksData($loaded, $storage);
|
||||
|
||||
}
|
||||
|
||||
if ($rendered) {
|
||||
$this->data['blocks']['rendered'] = $this->getBlocksData($rendered, $storage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getRenderedBlocks() {
|
||||
return $this->data['blocks']['rendered'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getRenderedBlocksCount() {
|
||||
return count($this->getRenderedBlocks());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getLoadedBlocks() {
|
||||
return $this->data['blocks']['loaded'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getLoadedBlocksCount() {
|
||||
return count($this->getLoadedBlocks());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName() {
|
||||
return 'blocks';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle() {
|
||||
return $this->t('Blocks');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPanelSummary() {
|
||||
return $this->t('Loaded: @loaded, rendered: @rendered', [
|
||||
'@loaded' => $this->getLoadedBlocksCount(),
|
||||
'@rendered' => $this->getRenderedBlocksCount()
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIcon() {
|
||||
return 'iVBORw0KGgoAAAANSUhEUgAAABUAAAAcCAYAAACOGPReAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2hpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowNDgwMTE3NDA3MjA2ODExOEY2MkNCNjI0NDY3NzkwRCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDowQjg5OTA4OEYwQTgxMUUzQkJDRThFQjA5Q0E1REFCRCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDowQjg5OTA4N0YwQTgxMUUzQkJDRThFQjA5Q0E1REFCRCIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1LjEgTWFjaW50b3NoIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MDM4MDExNzQwNzIwNjgxMTg3MUZDQ0I0RjY1RTlEM0IiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDQ4MDExNzQwNzIwNjgxMThGNjJDQjYyNDQ2Nzc5MEQiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz68h+kGAAAAWElEQVR42mL8//8/A7UBEwMNwAg3lAVEODg4UCW2Dhw4wDgapgyMtEj8eCMKFPCkyI1GFCJMiUnQVDUUX0SNllK4wxRf+JAdUeQUfaMRRd+ib4SHKUCAAQAMcyf8vLAstgAAAABJRU5ErkJggg==';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $decorator
|
||||
* @param $storage
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getBlocksData(EntityDecorator $decorator, EntityStorageInterface $storage) {
|
||||
$blocks = [];
|
||||
|
||||
/** @var \Drupal\block\BlockInterface $block */
|
||||
foreach ($decorator->getEntities() as $block) {
|
||||
/** @var Block $entity */
|
||||
if ($entity = $storage->load($block->get('id'))) {
|
||||
|
||||
$route = '';
|
||||
if ($entity->hasLinkTemplate('edit-form')) {
|
||||
$route = $entity->toUrl('edit-form')->toString();
|
||||
}
|
||||
|
||||
$id = $block->get('id');
|
||||
$blocks[$id] = [
|
||||
'id' => $id,
|
||||
'region' => $block->getRegion(),
|
||||
'status' => $block->get('status'),
|
||||
'theme' => $block->getTheme(),
|
||||
'plugin' => $block->get('plugin'),
|
||||
'settings' => $block->get('settings'),
|
||||
'route' => $route,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $blocks;
|
||||
}
|
||||
}
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\DataCollector;
|
||||
|
||||
use Drupal\webprofiler\DrupalDataCollectorInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
||||
|
||||
/**
|
||||
* Collects the used cache bins and cache CIDs.
|
||||
*/
|
||||
class CacheDataCollector extends DataCollector implements DrupalDataCollectorInterface {
|
||||
|
||||
use StringTranslationTrait, DrupalDataCollectorTrait;
|
||||
|
||||
const WEBPROFILER_CACHE_HIT = 'bin_cids_hit';
|
||||
const WEBPROFILER_CACHE_MISS = 'bin_cids_miss';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Exception $exception = NULL) {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->data['total'][CacheDataCollector::WEBPROFILER_CACHE_HIT] = 0;
|
||||
$this->data['total'][CacheDataCollector::WEBPROFILER_CACHE_MISS] = 0;
|
||||
$this->data['cache'] = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a cache get on a specific cache bin.
|
||||
*
|
||||
* @param $cache
|
||||
*/
|
||||
public function registerCacheHit($bin, $cache) {
|
||||
$current = isset($this->data['cache'][$bin][$cache->cid]) ? $this->data['cache'][$bin][$cache->cid] : NULL;
|
||||
|
||||
if (!$current) {
|
||||
$current = $cache;
|
||||
$current->{CacheDataCollector::WEBPROFILER_CACHE_HIT} = 0;
|
||||
$current->{CacheDataCollector::WEBPROFILER_CACHE_MISS} = 0;
|
||||
$this->data['cache'][$bin][$cache->cid] = $current;
|
||||
}
|
||||
|
||||
$current->{CacheDataCollector::WEBPROFILER_CACHE_HIT}++;
|
||||
$this->data['total'][CacheDataCollector::WEBPROFILER_CACHE_HIT]++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a cache get on a specific cache bin.
|
||||
*
|
||||
* @param $bin
|
||||
* @param $cid
|
||||
*/
|
||||
public function registerCacheMiss($bin, $cid) {
|
||||
$current = isset($this->data['cache'][$bin][$cid]) ?
|
||||
$this->data['cache'][$bin][$cid] : NULL;
|
||||
|
||||
if (!$current) {
|
||||
$current = new \StdClass();
|
||||
$current->{CacheDataCollector::WEBPROFILER_CACHE_HIT} = 0;
|
||||
$current->{CacheDataCollector::WEBPROFILER_CACHE_MISS} = 0;
|
||||
$this->data['cache'][$bin][$cid] = $current;
|
||||
}
|
||||
|
||||
$current->{CacheDataCollector::WEBPROFILER_CACHE_MISS}++;
|
||||
$this->data['total'][CacheDataCollector::WEBPROFILER_CACHE_MISS]++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to return the total amount of requested cache CIDS.
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getCacheCidsCount($type) {
|
||||
return $this->data['total'][$type];
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to return the total amount of hit cache CIDS.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getCacheHitsCount() {
|
||||
return $this->getCacheCidsCount(CacheDataCollector::WEBPROFILER_CACHE_HIT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to return the total amount of miss cache CIDS.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getCacheMissesCount() {
|
||||
return $this->getCacheCidsCount(CacheDataCollector::WEBPROFILER_CACHE_MISS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to return the total amount of hit cache CIDs keyed by bin.
|
||||
*
|
||||
* @param $type
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function cacheCids($type) {
|
||||
$hits = [];
|
||||
foreach ($this->data['cache'] as $bin => $caches) {
|
||||
$hits[$bin] = 0;
|
||||
foreach ($caches as $cid => $cache) {
|
||||
$hits[$bin] += $cache->{$type};
|
||||
}
|
||||
}
|
||||
|
||||
return $hits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to return hit cache CIDs keyed by bin.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCacheHits() {
|
||||
return $this->cacheCids(CacheDataCollector::WEBPROFILER_CACHE_HIT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to return miss cache CIDs keyed by bin.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCacheMisses() {
|
||||
return $this->cacheCids(CacheDataCollector::WEBPROFILER_CACHE_MISS);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName() {
|
||||
return 'cache';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle() {
|
||||
return $this->t('Cache');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPanelSummary() {
|
||||
return $this->t('Hit: @cache_hit, miss: @cache_miss', [
|
||||
'@cache_hit' => $this->getCacheCidsCount(CacheDataCollector::WEBPROFILER_CACHE_HIT),
|
||||
'@cache_miss' => $this->getCacheCidsCount(CacheDataCollector::WEBPROFILER_CACHE_MISS),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIcon() {
|
||||
return 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAcCAYAAABh2p9gAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNS4xIE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2Njc3QTVEQTkxNkMxMUUzQjA3OUEzQTNEMUVGMjVDOCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo2Njc3QTVEQjkxNkMxMUUzQjA3OUEzQTNEMUVGMjVDOCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjRGQTVBQzYxOTE2QzExRTNCMDc5QTNBM0QxRUYyNUM4IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjRGQTVBQzYyOTE2QzExRTNCMDc5QTNBM0QxRUYyNUM4Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+BsBwAAAAAJtJREFUeNpi/P//PwM1ARMDlcEIMdDBweEZjM0IihSgwEx8Gg4cOJCOrhGHOimqe5kF2QVYvDITl0vQvQwTo4oLkS1gQrPpPwiTEBkY6pnwKJ5JyOskJRvkcMUVxjgjhRhDsUUGSQZu3rwZb1j6+voyjhYOI9VAFmKTBTC3oMsTbyAx+RndAqxejo2NJdmL6HoYR6vRwWcgQIABAOn0PsqqgQzcAAAAAElFTkSuQmCC';
|
||||
}
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\DataCollector;
|
||||
|
||||
use Drupal\webprofiler\DrupalDataCollectorInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
||||
|
||||
/**
|
||||
* Provides a datacollector to show all requested configs.
|
||||
*/
|
||||
class ConfigDataCollector extends DataCollector implements DrupalDataCollectorInterface {
|
||||
|
||||
use StringTranslationTrait, DrupalDataCollectorTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Exception $exception = NULL) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new requested config name.
|
||||
*
|
||||
* @param string $name
|
||||
* The name of the config.
|
||||
*/
|
||||
public function addConfigName($name) {
|
||||
$this->data['config_names'][$name] = isset($this->data['config_names'][$name]) ? $this->data['config_names'][$name] + 1 : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to display the config names.
|
||||
*/
|
||||
public function getConfigNames() {
|
||||
return $this->data['config_names'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName() {
|
||||
return 'config';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle() {
|
||||
return $this->t('Config');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPanelSummary() {
|
||||
return $this->t('Total: @count', ['@count' => count($this->getConfigNames())]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIcon() {
|
||||
return 'iVBORw0KGgoAAAANSUhEUgAAABsAAAAcCAYAAACQ0cTtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNS4xIE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2Njc3QTVFMjkxNkMxMUUzQjA3OUEzQTNEMUVGMjVDOCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo2Njc3QTVFMzkxNkMxMUUzQjA3OUEzQTNEMUVGMjVDOCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjY2NzdBNUUwOTE2QzExRTNCMDc5QTNBM0QxRUYyNUM4IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjY2NzdBNUUxOTE2QzExRTNCMDc5QTNBM0QxRUYyNUM4Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+f0Re8gAAA3RJREFUeNrsVk9ME1kY/82fTqfQf5ROW2gpuKCioHbdEDeBxITdxE08eOHkiQOr8cZhNx7k4MGDmpAQxTtHPBgPXjCbeLDE/XtglSUGGyhxgBYLdNpSOu3MPN+MwUSQP1HUZONv8st7897M9833fb837zGEEHwusPiM+P86AxjmEqVKSfaBGuWvlHgf6cUUb9+65WhsbPzo706n0/j5wgWddgOUK1vSKEnSvjgyEQwGQW1xtBv+4jXjc7kcBgcH981gJpPZ3tmnjoa7NgOSXwYK2U+bRu76y73XLBaLIRQKvXe8paVlZ0c35L2ncWho6K3B/v5+JBIJOJ3Ot+PmfV9fnzWvcyJWpeNQ/O0oH7oILhK7Q4cnKR9RjlEWrCVtF0Xy/alTW5z19PRgbGwMo6OjlmHToekolUpheHgYhYL1Pso2F+a+OQd/cwTftdlRH9QhujPGconJx5/VyRMvpD+IWrxCa5bmeJ6/GolEtjibmppCuVyGz+dDd3e3ZTwajWJgYMAatyJiBcyGf0J7pwcdzQEcbktBnW8Fr3NMXcOSeLRpKSCw2cD0tNgEQ3+0q0DGx8ettre3FyMjI+/MrYhReKMcgpII1jePdZVBWQXcBxJYfOFGzsjjYGsyErYvdkLJnN1V+hMTE1Zrpm+jv4FVxoemhjISyWqsKi68JDWwcSUsPhWQSQBSdA4Lz+vh92j18rRyZs/rbLMjE0WaTSJlsab+h1LqNES7BqLpYB2vIPwYx/zjNqwlDsBR8VaRwlzzrs42FGlGtgWaBiVZjzLngVChNYQBGATas5OoKCvQSB6k4x64uXYGTwyW322dmao01WgqczNEujMRRYSjqhUOhwAbz4Klu4nB8yhyEtZyPEqh36BquSLYzpltBdLV1WWJwmzN6EzZb4aPUZCfVRHyeiA53Qi6XKhzuxGurUFD8Qz8Xga1BQ+y8SNLEKoe8jupcEOJ26HWSGN5VYYiJ3Ei9i14GpaNezOn8gIcjISn/3Sks8n2PyFqDzhd1y/XeL02URQ/4ExhwFOSMbtux0qxgoDHBb+7GiLPG9l8ce2vyYI8nQjHMXn/Fzy+uWzu1Ofpeze32/D29scVgLpjwMEfSrSVUe1PoUK/Yib+N/69+zuy8gJ9apGxzgcfCkLMmjspq3b4qZsH03XKPPP1kLofeC3AAMaxXzYck+ZeAAAAAElFTkSuQmCC';
|
||||
}
|
||||
}
|
||||
+253
@@ -0,0 +1,253 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\DataCollector;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\webprofiler\DrupalDataCollectorInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
||||
|
||||
/**
|
||||
* Class DatabaseDataCollector
|
||||
*/
|
||||
class DatabaseDataCollector extends DataCollector implements DrupalDataCollectorInterface {
|
||||
|
||||
use StringTranslationTrait, DrupalDataCollectorTrait;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
private $database;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||
*/
|
||||
private $configFactory;
|
||||
|
||||
/**
|
||||
* @param \Drupal\Core\Database\Connection $database
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
|
||||
*/
|
||||
public function __construct(Connection $database, ConfigFactoryInterface $configFactory) {
|
||||
$this->database = $database;
|
||||
$this->configFactory = $configFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Exception $exception = NULL) {
|
||||
$connections = [];
|
||||
foreach (Database::getAllConnectionInfo() as $key => $info) {
|
||||
$database = Database::getConnection('default', $key);
|
||||
$connections[$key] = $database->getLogger()->get('webprofiler');
|
||||
}
|
||||
|
||||
$this->data['connections'] = array_keys($connections);
|
||||
|
||||
$data = [];
|
||||
foreach ($connections as $key => $queries) {
|
||||
foreach ($queries as $query) {
|
||||
// Remove caller args.
|
||||
unset($query['caller']['args']);
|
||||
|
||||
// Remove query args element if empty.
|
||||
if (empty($query['args'])) {
|
||||
unset($query['args']);
|
||||
}
|
||||
|
||||
// Save time in milliseconds.
|
||||
$query['time'] = $query['time'] * 1000;
|
||||
$query['database'] = $key;
|
||||
$data[] = $query;
|
||||
}
|
||||
}
|
||||
|
||||
$querySort = $this->configFactory->get('webprofiler.config')
|
||||
->get('query_sort');
|
||||
if ('duration' === $querySort) {
|
||||
usort(
|
||||
$data, [
|
||||
"Drupal\\webprofiler\\DataCollector\\DatabaseDataCollector",
|
||||
"orderQueryByTime",
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$this->data['queries'] = $data;
|
||||
|
||||
$options = $this->database->getConnectionOptions();
|
||||
|
||||
// Remove password for security.
|
||||
unset($options['password']);
|
||||
|
||||
$this->data['database'] = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getDatabase() {
|
||||
return $this->data['database'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getQueryCount() {
|
||||
return count($this->data['queries']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getQueries() {
|
||||
return $this->data['queries'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total execution time.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getTime() {
|
||||
$time = 0;
|
||||
|
||||
foreach ($this->data['queries'] as $query) {
|
||||
$time += $query['time'];
|
||||
}
|
||||
|
||||
return $time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a color based on the number of executed queries.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getColorCode() {
|
||||
if ($this->getQueryCount() < 100) {
|
||||
return 'green';
|
||||
}
|
||||
if ($this->getQueryCount() < 200) {
|
||||
return 'yellow';
|
||||
}
|
||||
|
||||
return 'red';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configured query highlight threshold.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getQueryHighlightThreshold() {
|
||||
// When a profile is loaded from storage this object is deserialized and
|
||||
// no constructor is called so we cannot use dependency injection.
|
||||
return \Drupal::config('webprofiler.config')->get('query_highlight');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName() {
|
||||
return 'database';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle() {
|
||||
return $this->t('Database');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPanelSummary() {
|
||||
return $this->t('Executed queries: @count', ['@count' => $this->getQueryCount()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIcon() {
|
||||
return 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAcCAYAAABh2p9gAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQRJREFUeNpi/P//PwM1ARMDlcGogZQDlpMnT7pxc3NbA9nhQKxOpL5rQLwJiPeBsI6Ozl+YBOOOHTv+AOllQNwtLS39F2owKYZ/gRq8G4i3ggxEToggWzvc3d2Pk+1lNL4fFAs6ODi8JzdS7mMRVyDVoAMHDsANdAPiOCC+jCQvQKqBQB/BDbwBxK5AHA3E/kB8nKJkA8TMQBwLxaBIKQbi70AvTADSBiSadwFXpCikpKQU8PDwkGTaly9fHFigkaKIJid4584dkiMFFI6jkTJII0WVmpHCAixZQEXWYhDeuXMnyLsVlEQKI45qFBQZ8eRECi4DBaAlDqle/8A48ip6gAADANdQY88Uc0oGAAAAAElFTkSuQmCC';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLibraries() {
|
||||
return [
|
||||
'webprofiler/database',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getData() {
|
||||
$data = $this->data;
|
||||
|
||||
$conn = Database::getConnection();
|
||||
foreach ($data['queries'] as &$query) {
|
||||
$explain = TRUE;
|
||||
$type = 'select';
|
||||
|
||||
if (strpos($query['query'], 'INSERT') !== FALSE) {
|
||||
$explain = FALSE;
|
||||
$type = 'insert';
|
||||
}
|
||||
|
||||
if (strpos($query['query'], 'UPDATE') !== FALSE) {
|
||||
$explain = FALSE;
|
||||
$type = 'update';
|
||||
}
|
||||
|
||||
if (strpos($query['query'], 'CREATE') !== FALSE) {
|
||||
$explain = FALSE;
|
||||
$type = 'create';
|
||||
}
|
||||
|
||||
if (strpos($query['query'], 'DELETE') !== FALSE) {
|
||||
$explain = FALSE;
|
||||
$type = 'delete';
|
||||
}
|
||||
|
||||
$query['explain'] = $explain;
|
||||
$query['type'] = $type;
|
||||
|
||||
$quoted = [];
|
||||
foreach ((array) $query['args'] as $key => $val) {
|
||||
$quoted[$key] = is_null($val) ? 'NULL' : $conn->quote($val);
|
||||
}
|
||||
|
||||
$query['query_args'] = strtr($query['query'], $quoted);
|
||||
}
|
||||
|
||||
$data['query_highlight_threshold'] = $this->getQueryHighlightThreshold();
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $a
|
||||
* @param $b
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private function orderQueryByTime($a, $b) {
|
||||
$at = $a['time'];
|
||||
$bt = $b['time'];
|
||||
|
||||
if ($at == $bt) {
|
||||
return 0;
|
||||
}
|
||||
return ($at < $bt) ? 1 : -1;
|
||||
}
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Contains \Drupal\webprofiler\DataCollector\DevelDataCollector;
|
||||
*/
|
||||
|
||||
namespace Drupal\webprofiler\DataCollector;
|
||||
|
||||
use Drupal\Core\Menu\MenuLinkTreeInterface;
|
||||
use Drupal\Core\Menu\MenuTreeParameters;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\webprofiler\DrupalDataCollectorInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
||||
|
||||
/**
|
||||
* Class DevelDataCollector
|
||||
*/
|
||||
class DevelDataCollector extends DataCollector implements DrupalDataCollectorInterface {
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLinks() {
|
||||
return $this->data['links'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName() {
|
||||
return 'devel';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle() {
|
||||
return $this->t('Development');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasPanel() {
|
||||
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.
|
||||
*
|
||||
* @return string
|
||||
* The collector icon.
|
||||
*/
|
||||
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';
|
||||
}
|
||||
}
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\DataCollector;
|
||||
|
||||
use Drupal;
|
||||
use Drupal\Core\Routing\RedirectDestinationInterface;
|
||||
use Drupal\Core\Routing\UrlGeneratorInterface;
|
||||
use Drupal\webprofiler\DrupalDataCollectorInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
||||
use Symfony\Component\Process\Exception\ProcessFailedException;
|
||||
use Symfony\Component\Process\Exception\RuntimeException;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
/**
|
||||
* Class DrupalDataCollector
|
||||
*/
|
||||
class DrupalDataCollector extends DataCollector implements DrupalDataCollectorInterface {
|
||||
|
||||
use StringTranslationTrait, DrupalDataCollectorTrait;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Routing\RedirectDestinationInterface
|
||||
*/
|
||||
private $redirectDestination;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Routing\UrlGeneratorInterface
|
||||
*/
|
||||
private $urlGenerator;
|
||||
|
||||
/**
|
||||
* @param \Drupal\Core\Routing\RedirectDestinationInterface $redirectDestination
|
||||
* @param \Drupal\Core\Routing\UrlGeneratorInterface $urlGenerator
|
||||
*/
|
||||
public function __construct(RedirectDestinationInterface $redirectDestination, UrlGeneratorInterface $urlGenerator) {
|
||||
$this->redirectDestination = $redirectDestination;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Exception $exception = NULL) {
|
||||
$this->data['version'] = Drupal::VERSION;
|
||||
$this->data['profile'] = drupal_get_profile();
|
||||
$this->data['config_url'] = (new Drupal\Core\Url('webprofiler.settings', [], ['query' => $this->redirectDestination->getAsArray()]))->toString();
|
||||
|
||||
try {
|
||||
$process = new Process("git log -1 --pretty=format:'%H - %s (%ci)' --abbrev-commit");
|
||||
$process->setTimeout(3600);
|
||||
$process->mustRun();
|
||||
$this->data['git_commit'] = $process->getOutput();
|
||||
|
||||
$process = new Process("git log -1 --pretty=format:'%h' --abbrev-commit");
|
||||
$process->setTimeout(3600);
|
||||
$process->mustRun();
|
||||
$this->data['abbr_git_commit'] = $process->getOutput();
|
||||
} catch (ProcessFailedException $e) {
|
||||
$this->data['git_commit'] = $this->data['git_commit_abbr'] = NULL;
|
||||
} catch (RuntimeException $e) {
|
||||
$this->data['git_commit'] = $this->data['git_commit_abbr'] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getVersion() {
|
||||
return $this->data['version'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getProfile() {
|
||||
return $this->data['profile'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getConfigUrl() {
|
||||
return $this->data['config_url'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getGitCommit() {
|
||||
return $this->data['git_commit'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getAbbrGitCommit() {
|
||||
return $this->data['abbr_git_commit'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the collector.
|
||||
*
|
||||
* @return string The collector name
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getName() {
|
||||
return 'drupal';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle() {
|
||||
return $this->t('Drupal');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasPanel() {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIcon() {
|
||||
return 'iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNS4xIE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpCRDU5MUVDNzVGRTkxMUUzODdBMEJDOEZFRjA2QUY5MiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpCRDU5MUVDODVGRTkxMUUzODdBMEJDOEZFRjA2QUY5MiI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkJENTkxRUM1NUZFOTExRTM4N0EwQkM4RkVGMDZBRjkyIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkJENTkxRUM2NUZFOTExRTM4N0EwQkM4RkVGMDZBRjkyIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+iYsuIgAAAiNJREFUeNq8lj3KwkAQhhOxUPAnoIgWQiysbFLYG2/gEVJaegQ9QfQECh4g3kA9QbxBLMQ2KaxE2G/ffG5Yl/ytiguDJBn32Z15d2dUQojy0wGgDHQwGFjj8Vh7hxFyZIHNZtOt1+uuDPRtYK/Xs4rFIpyJDFQaiIm73a7NYMyq1arf7/dnWWApIN3VHBPzINHwHX5J4FxAKg6j0Wh4aSDREGb8TxqIXJVKJSIDY1Yul30RmgqE87swHkrDq2cCkYNareZ9AmPWarX2mUAk/hswZrgkUoGyIsm7y1ggDachnrNPrVKpEKSJBxZYnC+Xi/l4PL56T99uN/y8KDYCUpj2i2JRzONkWZYyGo0UXdeV4/GobDYb5Xw+J/obxv+mTqdT8rWTpND1ek3ihm3bRNO0F188O44T+UwmE+TQjBXNcDiciLD5fE7Shuu6xDTN0JdGgfi+//J9v98nA6EmqIoHYsJPxuFwIOKxiERDVxNQ4I4PNw1Ram6DIFAWiwUWG5uv+/2+E98V+IdOp7OiZzF6pitMhC2XyxDEfOKg1+t1ldnTtNtthxcB8sAGcgQRUbWGufM8LxQPfJBv3n+73Tq5qgVyibqWdYsASOUfLYyJJ679yKyHadDZbBYqkn8HMHaO3ydMl674rI8RayOACKUo/+l0SuAf12ZI9TRYLSZCJYk76GgbnyA9TxOlMpiqqrnuQhxk7igd8raJbKi/bvX/BBgANANSieJk+XMAAAAASUVORK5CYII=';
|
||||
}
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\DataCollector;
|
||||
|
||||
/**
|
||||
* Class DrupalDataCollectorTrait
|
||||
*/
|
||||
trait DrupalDataCollectorTrait {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPanelSummary() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasPanel() {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLibraries() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDrupalSettings() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getData() {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $class
|
||||
* @param $method
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function getMethodData($class, $method) {
|
||||
$class = is_object($class) ? get_class($class) : $class;
|
||||
$data = [];
|
||||
|
||||
try {
|
||||
$reflectedMethod = new \ReflectionMethod($class, $method);
|
||||
|
||||
$data = [
|
||||
'class' => $class,
|
||||
'method' => $method,
|
||||
'file' => $reflectedMethod->getFilename(),
|
||||
'line' => $reflectedMethod->getStartLine(),
|
||||
];
|
||||
} catch (\ReflectionException $re) {
|
||||
// TODO: handle the exception.
|
||||
} finally {
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $value
|
||||
*
|
||||
* @return int|string
|
||||
*/
|
||||
private function convertToBytes($value) {
|
||||
if ('-1' === $value) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
$value = strtolower($value);
|
||||
$max = strtolower(ltrim($value, '+'));
|
||||
if (0 === strpos($max, '0x')) {
|
||||
$max = intval($max, 16);
|
||||
}
|
||||
elseif (0 === strpos($max, '0')) {
|
||||
$max = intval($max, 8);
|
||||
}
|
||||
else {
|
||||
$max = intval($max);
|
||||
}
|
||||
|
||||
switch (substr($value, -1)) {
|
||||
case 't':
|
||||
$max *= 1024;
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
$max *= 1024;
|
||||
break;
|
||||
|
||||
case 'm':
|
||||
$max *= 1024;
|
||||
break;
|
||||
|
||||
case 'k':
|
||||
$max *= 1024;
|
||||
break;
|
||||
}
|
||||
|
||||
return $max;
|
||||
}
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\DataCollector;
|
||||
|
||||
use Drupal\webprofiler\DrupalDataCollectorInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Symfony\Component\HttpKernel\DataCollector\EventDataCollector as BaseEventDataCollector;
|
||||
|
||||
/**
|
||||
* Class EventsDataCollector
|
||||
*/
|
||||
class EventsDataCollector extends BaseEventDataCollector implements DrupalDataCollectorInterface {
|
||||
|
||||
use StringTranslationTrait, DrupalDataCollectorTrait;
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getCalledListenersCount() {
|
||||
return count($this->getCalledListeners());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getNotCalledListenersCount() {
|
||||
return count($this->getNotCalledListeners());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setCalledListeners(array $listeners) {
|
||||
$listeners = $this->computePriority($listeners);
|
||||
$this->data['called_listeners'] = $listeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the priority value to the $listeners array.
|
||||
*
|
||||
* @param array $listeners
|
||||
* @return array
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle() {
|
||||
return $this->t('Events');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPanelSummary() {
|
||||
return $this->t('Called listeners: @listeners', ['@listeners' => count($this->getCalledListeners())]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIcon() {
|
||||
return 'iVBORw0KGgoAAAANSUhEUgAAABUAAAAcCAYAAACOGPReAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABFJJREFUeNrkVVlIY2cY/RMTE81NMkkajUs1OBqkiVsjjAtStGrtSGyFjOjAQNVCKRb66ot9KrjgQx+FUgTBKkURbIfighWl4r6h44pajcZEo3ESTeKS9PzB2AyNZaD1qRcOem+S83/f+c53Lsvj8ZD/+mKTB7gehJTj+2d9fZ1MTk6S0NBQSW9vb97e3t7jmpqaXzIzM185HA7vd4KDg8nGxoaysbGxVCwWm/V6/aDL5TKlpKSQpKSkv5NyuVxyc3Mj7e7u/jw2NjYxJyfnMDIykmGz2UQgEBAWi0XcbjeRSqWhZWVl4v39fXVXV5cqNzf3exxmCNj+9fU1MzQ09JVWq32sUqmMu7u7QhwiDwoKIoeHh2R7e5twOByCwcrQhUShUJjz8vJkw8PDX5+fn8sDkvb3938YHR39rlAoNBoMBgGqtWxubnJRKbu9vZ20trZSQoJnvKioKMvZ2Rn/6urKmpqayvT19ekCks7NzaUnJyeboK0kPj7+cGZmJprH4zGnp6duEBFUTg4ODqjmIfPz87GQxoRnori4ODOKUPuTsnw+RRvPGIYJMZvNDNplYmJiLvPz839oamoSj4yMfAJNuRqN5mV9ff0fOPDF1NSUAt85lclkDkjnys7O/vGOlZLeQgjIgUggnmqHqmMqKip+z8jI8MAFnpKSkpXZ2dn38JkIUAFRQNjt/R2Xv09twBFwAGwClunp6efLy8tZdFgUW1tbiaOjo1/is9fUhcA+YL69fzvzSyQSEQZHfBJBT4J2Bf9qo9Rq9bxcLndeXl4STJrA8B4Mc/atN4pesAk5OTkh1PB0exYXF/kWi4UTFhZG+Hw+wZQJ5BDR7fEPIroYASu9uLggJpOJYO2I0+kkqI47Njb2MdzAKS4uXisvL5/FurIGBgaeYoDS1dVVsrKyQpaWlghsF7hS2IJERER4T4U/qckT4ccP6BYplco+rOcxqn0fZFqj0fgkLS3tV18m0EICktJV9F101xcWFj5Cu+HQ1YGNoeSXWGErpv8IwVOSlZXVh7xw0zy4V1MY3/uXWgetMzB8EZUHw7lKSEjgQ0MONLei2kcTExN5R0dHMehshw7x3umLRKI7YDhaDOSJ18hstq2qquobLMG30DKYkuzs7KggTa5Pf4p/rJReSCud1WplEBYuSMGrra39FG1ywsPDgwsLC+0YFoMAKi0qKupA5c57K0V1XjsdHx+/g6mXUksVFBS8wmF23FeMj48/w7PXiLsxePcG65qPDNCsra15XRCQFNP1AgRPMaA4aOvp6OjQ2O12cVtb20vE389YAHFLS0sO2vbYbLYQHKRHShEEy5ul+kIAe02Q5vy6urouTNyDV8VNT0/PBGzzxW1wRIHsM7T+W3V1tROvEE9lZeUCKlVgSfyD6S9SGsKdnZ1pOp3OkJ6efj04OPgTnmsAlv8PACXa/Q4L4UByuZqbm/UNDQ1vkLL+3+/9ByH9U4ABADscgvUMKuLiAAAAAElFTkSuQmCC';
|
||||
}
|
||||
}
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\DataCollector;
|
||||
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Extension\ThemeHandlerInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\webprofiler\DrupalDataCollectorInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
||||
|
||||
/**
|
||||
* Defines a data collector for the extension system.
|
||||
*/
|
||||
class ExtensionDataCollector extends DataCollector implements DrupalDataCollectorInterface {
|
||||
|
||||
use StringTranslationTrait, DrupalDataCollectorTrait;
|
||||
|
||||
/**
|
||||
* The module handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* The theme handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ThemeHandlerInterface
|
||||
*/
|
||||
protected $themeHandler;
|
||||
|
||||
/**
|
||||
* The app root.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $root;
|
||||
|
||||
/**
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler.
|
||||
* @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
|
||||
* The theme handler.
|
||||
* @param string $root
|
||||
* The app root.
|
||||
*/
|
||||
public function __construct(ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler, $root) {
|
||||
$this->moduleHandler = $module_handler;
|
||||
$this->themeHandler = $theme_handler;
|
||||
$this->root = $root;
|
||||
|
||||
$this->data['drupal_extension']['modules'] = [];
|
||||
$this->data['drupal_extension']['themes'] = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Exception $exception = NULL) {
|
||||
$modules = $this->moduleHandler->getModuleList();
|
||||
$themes = $this->themeHandler->listInfo();
|
||||
|
||||
$this->data['drupal_extension']['count'] = count($modules) + count($themes);
|
||||
$this->data['drupal_extension']['modules'] = $modules;
|
||||
$this->data['drupal_extension']['themes'] = $themes;
|
||||
$this->data['drupal_extension']['installation_path'] = $this->root . '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of active extensions.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getExtensionsCount() {
|
||||
return isset($this->data['drupal_extension']['count']) ? $this->data['drupal_extension']['count'] : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of active modules.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getModulesCount() {
|
||||
return count($this->data['drupal_extension']['modules']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of active themes.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getThemesCount() {
|
||||
return count($this->data['drupal_extension']['themes']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName() {
|
||||
return 'drupal_extension';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle() {
|
||||
return $this->t('Extensions');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPanelSummary() {
|
||||
return $this->t('Total: @extensions', ['@extensions' => $this->getExtensionsCount()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIcon() {
|
||||
return 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAcCAYAAABh2p9gAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNS4xIE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo0RkE1QUM1RjkxNkMxMUUzQjA3OUEzQTNEMUVGMjVDOCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo0RkE1QUM2MDkxNkMxMUUzQjA3OUEzQTNEMUVGMjVDOCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjRGQTVBQzVEOTE2QzExRTNCMDc5QTNBM0QxRUYyNUM4IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjRGQTVBQzVFOTE2QzExRTNCMDc5QTNBM0QxRUYyNUM4Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+7LqYtwAAAUBJREFUeNpi/P//PwM1ARMDlcGogYPQQBYQ4eDgQIza/SClQNwIxA3okgcOHCDZhRVAfA6I64F4EjW8fBLqwn1AnAvE8aQaqAc1QAZJ7DMQRwPxayDuAGIBYgwUAeL1QHwWGm6XgLgUSf4FEPcAsQQQ+xBjICh8AmARBgSCQNwFxEFIatYCMagQcEHXzGhvb09s6bAdiL2gbHYg/oEm/x8Y00ykRAofEvsvzlgGmsoIpIWBWBaIlYD4Bg61B5DY/ED8HRrjIH2yINchh+E7IH4CxPeBuAAam8jgBBD3IfEtgZgTiPdC9T1BySloYCcQ20DTmTg0tuegWVICjZSNWLMeFgBKKsU45EDi9kA8H4ivEmsgNsAKxEXQBH0TiMtwFg5EgvnQXHIFmibfUGrgeaghTdBIxAoYR2u9EWAgQIABAKKeRzEX0gXIAAAAAElFTkSuQmCC';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getData() {
|
||||
$data = $this->data;
|
||||
|
||||
// Copy protected properties over public ones to
|
||||
// let json_encode to find them.
|
||||
$this->copyToPublic($data['drupal_extension']['modules']);
|
||||
$this->copyToPublic($data['drupal_extension']['themes']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies protected properties to public ones.
|
||||
*
|
||||
* @param \Drupal\Core\Extension\Extension[] $extensions
|
||||
*/
|
||||
private function copyToPublic($extensions) {
|
||||
foreach ($extensions as &$extension) {
|
||||
$extension->public_type = $extension->getType();
|
||||
$extension->public_name = $extension->getName();
|
||||
$extension->public_path = $extension->getPath();
|
||||
$extension->public_pathname = $extension->getPathname();
|
||||
$extension->public_filename = $extension->getFilename();
|
||||
$extension->public_extension_pathname = $extension->getExtensionPathname();
|
||||
$extension->public_extension_filename = $extension->getExtensionFilename();
|
||||
}
|
||||
}
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\DataCollector;
|
||||
|
||||
use Drupal\webprofiler\DrupalDataCollectorInterface;
|
||||
use Drupal\webprofiler\Form\FormBuilderWrapper;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
||||
|
||||
/**
|
||||
* Class FormsDataCollector
|
||||
*/
|
||||
class FormsDataCollector extends DataCollector implements DrupalDataCollectorInterface {
|
||||
|
||||
use StringTranslationTrait, DrupalDataCollectorTrait;
|
||||
|
||||
/**
|
||||
* @var \Drupal\webprofiler\Form\FormBuilderWrapper
|
||||
*/
|
||||
private $formBuilder;
|
||||
|
||||
/**
|
||||
* @param \Drupal\webprofiler\Form\FormBuilderWrapper $formBuilder
|
||||
*/
|
||||
public function __construct(FormBuilderWrapper $formBuilder) {
|
||||
$this->formBuilder = $formBuilder;
|
||||
|
||||
$this->data['forms'] = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Exception $exception = NULL) {
|
||||
$this->data['forms'] = $this->formBuilder->getBuildForm();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getForms() {
|
||||
return $this->data['forms'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getFormsCount() {
|
||||
return count($this->getForms());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName() {
|
||||
return 'forms';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle() {
|
||||
return $this->t('Forms');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPanelSummary() {
|
||||
return $this->t('Rendered: @forms', ['@forms' => $this->getFormsCount()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIcon() {
|
||||
return 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAcCAYAAABh2p9gAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNS4xIE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo0RkE1QUM1QjkxNkMxMUUzQjA3OUEzQTNEMUVGMjVDOCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo0RkE1QUM1QzkxNkMxMUUzQjA3OUEzQTNEMUVGMjVDOCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjRGQTVBQzU5OTE2QzExRTNCMDc5QTNBM0QxRUYyNUM4IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjRGQTVBQzVBOTE2QzExRTNCMDc5QTNBM0QxRUYyNUM4Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+I7130QAAAKRJREFUeNrsVVsKxCAMzIgH8897eDHxGP55LX+0RmjpYxHdyrLsNl9iknEyExA5Z5oZgibH9wPKVlJrnVNKRwZCkPceQ4AhhOpUjJFaeaXUBRjssrW2FvDZGINS2GV9AQb3rpvCvZWhc24rKpdDmp17P2MKjzIFEAD16vdDi73Xbx2/pelZItnz6oiusneB32b4is0Iw8eUfzBlOiCef/l2LAIMAGACWQCJ5bXFAAAAAElFTkSuQmCC';
|
||||
}
|
||||
}
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\DataCollector;
|
||||
|
||||
use Drupal\webprofiler\Http\HttpClientMiddleware;
|
||||
use Drupal\webprofiler\DrupalDataCollectorInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
||||
|
||||
/**
|
||||
* Collects data about http calls during request.
|
||||
*/
|
||||
class HttpDataCollector extends DataCollector implements DrupalDataCollectorInterface {
|
||||
|
||||
use StringTranslationTrait, DrupalDataCollectorTrait;
|
||||
|
||||
/**
|
||||
* @var \GuzzleHttp\Client
|
||||
*/
|
||||
private $middleware;
|
||||
|
||||
/**
|
||||
* @param \Drupal\webprofiler\Http\HttpClientMiddleware $middleware
|
||||
*/
|
||||
public function __construct(HttpClientMiddleware $middleware) {
|
||||
$this->middleware = $middleware;
|
||||
|
||||
$this->data['completed'] = [];
|
||||
$this->data['failed'] = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Exception $exception = NULL) {
|
||||
$completed = $this->middleware->getCompletedRequests();
|
||||
$failed = $this->middleware->getFailedRequests();
|
||||
|
||||
foreach ($completed as $data) {
|
||||
/** @var \GuzzleHttp\Psr7\Request $request */
|
||||
$request = $data['request'];
|
||||
/** @var \GuzzleHttp\Psr7\Response $response */
|
||||
$response = $data['response'];
|
||||
/** @var \GuzzleHttp\TransferStats $stats */
|
||||
$stats = $request->stats;
|
||||
|
||||
$uri = $request->getUri();
|
||||
$this->data['completed'][] = [
|
||||
'request' => [
|
||||
'method' => $request->getMethod(),
|
||||
'uri' => [
|
||||
'schema' => $uri->getScheme(),
|
||||
'host' => $uri->getHost(),
|
||||
'port' => $uri->getPort(),
|
||||
'path' => $uri->getPath(),
|
||||
'query' => $uri->getQuery(),
|
||||
'fragment' => $uri->getFragment(),
|
||||
],
|
||||
'headers' => $request->getHeaders(),
|
||||
'protocol' => $request->getProtocolVersion(),
|
||||
'request_target' => $request->getRequestTarget(),
|
||||
'stats' => [
|
||||
'transferTime' => $stats->getTransferTime(),
|
||||
'handlerStats' => $stats->getHandlerStats()
|
||||
],
|
||||
],
|
||||
'response' => [
|
||||
'phrase' => $response->getReasonPhrase(),
|
||||
'status' => $response->getStatusCode(),
|
||||
'headers' => $response->getHeaders(),
|
||||
'protocol' => $response->getProtocolVersion(),
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($failed as $data) {
|
||||
/** @var \GuzzleHttp\Psr7\Request $request */
|
||||
$request = $data['request'];
|
||||
/** @var \GuzzleHttp\Psr7\Response $response */
|
||||
$response = $data['response'];
|
||||
|
||||
$uri = $request->getUri();
|
||||
$this->data['failed'][] = [
|
||||
'request' => [
|
||||
'method' => $request->getMethod(),
|
||||
'uri' => [
|
||||
'schema' => $uri->getScheme(),
|
||||
'host' => $uri->getHost(),
|
||||
'port' => $uri->getPort(),
|
||||
'path' => $uri->getPath(),
|
||||
'query' => $uri->getQuery(),
|
||||
'fragment' => $uri->getFragment(),
|
||||
],
|
||||
'headers' => $request->getHeaders(),
|
||||
'protocol' => $request->getProtocolVersion(),
|
||||
'request_target' => $request->getRequestTarget(),
|
||||
],
|
||||
'response' => [
|
||||
'phrase' => $response->getReasonPhrase(),
|
||||
'status' => $response->getStatusCode(),
|
||||
'headers' => $response->getHeaders(),
|
||||
'protocol' => $response->getProtocolVersion(),
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getCompletedRequestsCount() {
|
||||
return count($this->getCompletedRequests());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getCompletedRequests() {
|
||||
return $this->data['completed'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getFailedRequestsCount() {
|
||||
return count($this->getFailedRequests());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getFailedRequests() {
|
||||
return $this->data['failed'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName() {
|
||||
return 'http';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle() {
|
||||
return $this->t('Http');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPanelSummary() {
|
||||
return $this->t('Completed @completed, error @error', [
|
||||
'@completed' => $this->getCompletedRequestsCount(),
|
||||
'@error' => $this->getFailedRequestsCount()
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIcon() {
|
||||
return 'iVBORw0KGgoAAAANSUhEUgAAABUAAAAcCAYAAACOGPReAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAATlJREFUeNrsleERgjAMha0TdISOwAZ2BEZghI7ABo7AOQFuwAjoBLgBblBbfb0LudByp/4jdw+PNnxtkqYq7/3h13Y8/MF26B9spfo6yAX1QSa6EY2Y0xLrzROgddAMOcgLmuFbhDqyG00W8Rm1JWgEWCG0oQBuJKjGigNUs/xWUJdJheZQJzhZFGkgKWkw1mM8bmTCvOPQcSVXrTA+Ydc0kujXJGg6p5VwrG5BJzb2CLriN9kT74afUylPloQ+kdDPELXpg1qGvwbtURwjFGkkC8RIZw6d2QeO7MII81wRbDm0Z068Zf0G1bxQl8z1UH1zoQwk9NRZdkPoHt+KbZoqa+HYZS4T3iimdEvRDrIF4KIRclBNjk+uSB2/eBJUvR9KSek26BZHOit2zl3oqkV91P6//3N7CTAAIIc/qj2gy4gAAAAASUVORK5CYII=';
|
||||
}
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\DataCollector;
|
||||
|
||||
use Drupal\Core\Mail\MailInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\webprofiler\DrupalDataCollectorInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
||||
|
||||
/**
|
||||
* Class MailDataCollector
|
||||
*/
|
||||
class MailDataCollector extends DataCollector implements DrupalDataCollectorInterface {
|
||||
|
||||
use StringTranslationTrait, DrupalDataCollectorTrait;
|
||||
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
private $messages;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->messages = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects data for the given Request and Response.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
* @param Response $response A Response instance
|
||||
* @param \Exception $exception An Exception instance
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Exception $exception = NULL) {
|
||||
$this->data['mail'] = $this->messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $message
|
||||
* @param \Drupal\Core\Mail\MailInterface $mail
|
||||
*/
|
||||
public function addMessage($message, MailInterface $mail) {
|
||||
$class = get_class($mail);
|
||||
$method = $this->getMethodData($class, 'mail');
|
||||
|
||||
$this->messages[] = [
|
||||
'message' => $message,
|
||||
'method' => $method,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getMailSent() {
|
||||
return count($this->data['mail']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the collector.
|
||||
*
|
||||
* @return string The collector name
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getName() {
|
||||
return 'mail';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the datacollector title.
|
||||
*
|
||||
* @return string
|
||||
* The datacollector title.
|
||||
*/
|
||||
public function getTitle() {
|
||||
return $this->t('Mail');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPanelSummary() {
|
||||
return $this->t('Total: @count', ['@count' => $this->getMailSent()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIcon() {
|
||||
return 'iVBORw0KGgoAAAANSUhEUgAAABcAAAAcCAYAAACK7SRjAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6N0NEOTU1MjM0OThFMTFFMDg3NzJBNTE2ODgwQzMxMzQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6N0NEOTU1MjQ0OThFMTFFMDg3NzJBNTE2ODgwQzMxMzQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDoxMEQ5OTQ5QzQ5OEMxMUUwODc3MkE1MTY4ODBDMzEzNCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3Q0Q5NTUyMjQ5OEUxMUUwODc3MkE1MTY4ODBDMzEzNCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PpkRnSAAAAJ0SURBVHjaYvz//z8DrQATAw3BqOFYAaO9vT1FBhw4cGCAXA5MipxBQUHT3r17l0AVAxkZ/wkLC89as2ZNIcjlYkALXKnlWqBZTH/+/PEDmQsynLW/v3+NoaHhN2oYDjJn8uTJK4BMNpDhPwsLCwOKiop2+fn5vafEYC8vrw8gc/Lz8wOB3B8gw/nev38vn5WV5eTg4LA/Ly/vESsrK2npmYmJITU19SnQ8L0gc4DxpwgyF2S4EEjB58+f+crLy31YWFjOt7S0XBYUFPxHjMEcHBz/6+rqboqJiZ0qKSnxBpkDlRICGc4MU/j792+2CRMm+L18+fLSxIkTDykoKPzBZ7CoqOi/np6eE8rKylvb29v9fvz4wYEkzYKRzjk5OX/LyMjcnDRpEkjjdisrK6wRraOj8wvokAMLFy788ejRoxcaGhrPCWai4ODgB8DUE3/mzBknYMToASNoMzAfvEVW4+Tk9LmhoWFbTU2NwunTpx2BjiiMjo6+hm4WCzJHUlLyz+vXrxkfP36sDOI/ffpUPikpibe7u3sLsJjQW7VqlSrQxe+Avjmanp7u9PbtWzGQOmCCkARmmu/m5uYfT548yY/V5UpKSl+2b9+uiiz26dMnIWBSDTp27NgdYGrYCIzwE7m5uR4wg2Hg/PnzSsDI/QlKOcjZ3wGUBLm5uf+DwLdv38gub4AG/xcSEvr35s0bZmCB5sgCE/z69SsjyDJKMtG/f/8YQQYD8wkoGf8H51AbG5sH1CpbQBnQ09PzBiiHgoIFFHlBQGwLxLxUMP8dqJgH4k3gIhfIkAKVYkDMTmmhCHIxEL8A4peMo02L4WU4QIABANxZAoDIQDv3AAAAAElFTkSuQmCC';
|
||||
}
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\DataCollector;
|
||||
|
||||
use Drupal\webprofiler\DrupalDataCollectorInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\webprofiler\Frontend\PerformanceTimingData;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
||||
|
||||
/**
|
||||
* Collects data about frontend performance.
|
||||
*/
|
||||
class PerformanceTimingDataCollector extends DataCollector implements DrupalDataCollectorInterface {
|
||||
|
||||
use StringTranslationTrait, DrupalDataCollectorTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Exception $exception = NULL) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
*/
|
||||
public function setData($data) {
|
||||
$this->data['performance'] = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName() {
|
||||
return 'performance_timing';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle() {
|
||||
return $this->t('Performance Timing');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPanelSummary() {
|
||||
if (isset($this->data['performance'])) {
|
||||
$performanceData = new PerformanceTimingData($this->data['performance']);
|
||||
return $this->t('TTFB: @ttfb', ['@ttfb' => sprintf('%.0f ms', $performanceData->getTtfbTiming())]);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIcon() {
|
||||
return 'iVBORw0KGgoAAAANSUhEUgAAABUAAAAcCAYAAACOGPReAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQJJREFUeNpi/P//PwO1ARMDDcCooUPEUEYi1AgAcQIQ+0P5H4B4IxAvwKaYmCSqAMT7gbgBajhMrB8qLkCsoQFQQ0CuOw/EBjgsLIAajmEouvdBhukD8UQgdkASwwXuA7EhNEhwuvQ8iXHSj2Q53FBY7BtADVxIoqEfoQYnYJPEF3bEROZ6WDDBvO+ALcCxJCsBAmpA4SuA7P2PBDQUEOGTDTA1TNCYs6dCRgIlxQswQ0GMB0A8nwgv4gqa+VCXgpMWC1QiEerF9WgaDmJJp/OhkUNIHUHQgJ4ecQHkiMKXXALQIowqpdR8pJi/AA0qvC4lFsyHYqK8zzhaRQ8NQwECDABNaU12xhTp2QAAAABJRU5ErkJggg==';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getData() {
|
||||
$data = $this->data;
|
||||
|
||||
if (isset($this->data['performance'])) {
|
||||
$performanceData = new PerformanceTimingData($this->data['performance']);
|
||||
$data['performance']['computed']['DNS lookup time'] = $performanceData->getDNSTiming();
|
||||
$data['performance']['computed']['TCP handshake time'] = $performanceData->getTCPTiming();
|
||||
$data['performance']['computed']['Time to first byte'] = $performanceData->getTtfbTiming();
|
||||
$data['performance']['computed']['Data download time'] = $performanceData->getDataTiming();
|
||||
$data['performance']['computed']['DOM building time'] = $performanceData->getDomTiming();
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\DataCollector;
|
||||
|
||||
use Drupal\Core\Routing\LinkGeneratorTrait;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\webprofiler\DrupalDataCollectorInterface;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Provides a data collector to collect all kind of php information.
|
||||
*/
|
||||
class PhpConfigDataCollector extends DataCollector implements DrupalDataCollectorInterface {
|
||||
|
||||
use StringTranslationTrait, DrupalDataCollectorTrait, LinkGeneratorTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Exception $exception = NULL) {
|
||||
$this->data = [
|
||||
'token' => $response->headers->get('X-Debug-Token'),
|
||||
'php_version' => PHP_VERSION,
|
||||
'xdebug_enabled' => extension_loaded('xdebug'),
|
||||
'xhprof_enabled' => extension_loaded('xhprof'),
|
||||
'eaccel_enabled' => extension_loaded('eaccelerator') && ini_get('eaccelerator.enable'),
|
||||
'apc_enabled' => extension_loaded('apc') && ini_get('apc.enabled'),
|
||||
'xcache_enabled' => extension_loaded('xcache') && ini_get('xcache.cacher'),
|
||||
'wincache_enabled' => extension_loaded('wincache') && ini_get('wincache.ocenabled'),
|
||||
'zend_opcache_enabled' => extension_loaded('Zend OPcache') && ini_get('opcache.enable'),
|
||||
'sapi_name' => php_sapi_name()
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the token.
|
||||
*
|
||||
* @return string The token
|
||||
*/
|
||||
public function getToken() {
|
||||
return $this->data['token'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the PHP version.
|
||||
*
|
||||
* @return string The PHP version
|
||||
*/
|
||||
public function getPhpVersion() {
|
||||
return $this->data['php_version'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the XDebug is enabled.
|
||||
*
|
||||
* @return Boolean true if XDebug is enabled, false otherwise
|
||||
*/
|
||||
public function hasXDebug() {
|
||||
return $this->data['xdebug_enabled'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the XHProf is enabled.
|
||||
*
|
||||
* @return Boolean true if XHProf is enabled, false otherwise
|
||||
*/
|
||||
public function hasXHProf() {
|
||||
return $this->data['xhprof_enabled'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if EAccelerator is enabled.
|
||||
*
|
||||
* @return Boolean true if EAccelerator is enabled, false otherwise
|
||||
*/
|
||||
public function hasEAccelerator() {
|
||||
return $this->data['eaccel_enabled'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if APC is enabled.
|
||||
*
|
||||
* @return Boolean true if APC is enabled, false otherwise
|
||||
*/
|
||||
public function hasApc() {
|
||||
return $this->data['apc_enabled'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if Zend OPcache is enabled
|
||||
*
|
||||
* @return Boolean true if Zend OPcache is enabled, false otherwise
|
||||
*/
|
||||
public function hasZendOpcache() {
|
||||
return $this->data['zend_opcache_enabled'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if XCache is enabled.
|
||||
*
|
||||
* @return Boolean true if XCache is enabled, false otherwise
|
||||
*/
|
||||
public function hasXCache() {
|
||||
return $this->data['xcache_enabled'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if WinCache is enabled.
|
||||
*
|
||||
* @return Boolean true if WinCache is enabled, false otherwise
|
||||
*/
|
||||
public function hasWinCache() {
|
||||
return $this->data['wincache_enabled'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if any accelerator is enabled.
|
||||
*
|
||||
* @return Boolean true if any accelerator is enabled, false otherwise
|
||||
*/
|
||||
public function hasAccelerator() {
|
||||
return $this->hasApc() || $this->hasZendOpcache() || $this->hasEAccelerator() || $this->hasXCache() || $this->hasWinCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the PHP SAPI name.
|
||||
*
|
||||
* @return string The environment
|
||||
*/
|
||||
public function getSapiName() {
|
||||
return $this->data['sapi_name'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle() {
|
||||
return $this->t('PHP Config');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPanelSummary() {
|
||||
return $this->t('PHP: @version', ['@version' => $this->getPhpVersion()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName() {
|
||||
return 'php_config';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIcon() {
|
||||
return 'iVBORw0KGgoAAAANSUhEUgAAABUAAAAcCAMAAAC5xgRsAAAAZlBMVEX///////////////////////////////////////////////////////////////////////////////////////////+ZmZmZmZlISEhJSUmdnZ1HR0fR0dFZWVlpaWlfX18/Pz+puygPAAAAIXRSTlMACwwlJygpLzIzNjs8QEtMUmd6e32AucDBw8fIydTm6u5l8MjvAAAAo0lEQVR42r2P2Q6CMBBFL6XsZRGRfZv//0nbDBNEE19MnJeTc5ILKf58ahiUwzy/AJpIWwREwQnEXRdbGCLjrO+djWRvVMiJcigxB7viGogxDdJpSmHEmCVPS7YczJvgUu+CS30IvtbNYZMvsGVo2mVpG/kbm4auiCpdcC3YPCAhSpAdUzaAn6qPKZtUT6ZSzb4bi2hdo9MQ1nX4ASjfV+/4/Z40pyCHrNTbIgAAAABJRU5ErkJggg==';
|
||||
}
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\DataCollector;
|
||||
|
||||
use Drupal\Core\Controller\ControllerResolverInterface;
|
||||
use Drupal\webprofiler\DrupalDataCollectorInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\DataCollector\RequestDataCollector as BaseRequestDataCollector;
|
||||
|
||||
/**
|
||||
* Integrate _content into the RequestDataCollector
|
||||
*/
|
||||
class RequestDataCollector extends BaseRequestDataCollector implements DrupalDataCollectorInterface {
|
||||
|
||||
use StringTranslationTrait, DrupalDataCollectorTrait;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Controller\ControllerResolverInterface
|
||||
*/
|
||||
private $controllerResolver;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $accessCheck;
|
||||
|
||||
/**
|
||||
* @param \Drupal\Core\Controller\ControllerResolverInterface $controllerResolver
|
||||
*/
|
||||
public function __construct(ControllerResolverInterface $controllerResolver) {
|
||||
parent::__construct();
|
||||
|
||||
$this->controllerResolver = $controllerResolver;
|
||||
$this->accessCheck = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Exception $exception = NULL) {
|
||||
parent::collect($request, $response, $exception);
|
||||
|
||||
$controller = $this->controllerResolver->getController($request);
|
||||
|
||||
$this->data['controller'] = $this->getMethodData($controller[0], $controller[1]);
|
||||
$this->data['access_check'] = $this->accessCheck;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $service_id
|
||||
* @param $callable
|
||||
* @param $request
|
||||
*/
|
||||
public function addAccessCheck($service_id, $callable, Request $request) {
|
||||
$this->accessCheck[$request->getPathInfo()][] = [
|
||||
'service_id' => $service_id,
|
||||
'callable' => $this->getMethodData($callable[0], $callable[1]),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle() {
|
||||
return $this->t('Request');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPanelSummary() {
|
||||
return $this->data['status_code'].' '.$this->data['status_text'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIcon() {
|
||||
return 'iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAQAAADYBBcfAAACvElEQVR42tVTbUhTYRTerDCnKVoUUr/KCZmypA9Koet0bXNLJ5XazDJ/WFaCUY0pExRZXxYiJgsxWWjkaL+yK+po1gjyR2QfmqWxtBmaBtqWGnabT++c11Fu4l/P4VzOPc95zoHznsNZodIbLDdRcKnc1Bu8DAK45ZsOnykQNMopsNooLxCknb0cDq5vml9FtHiIgpBR0R6iihYyFMTDt2Lg56ObPkI6TMGXSof1EV67IqCwisJSWliFAG/E0CfFIiebdNypcxi/1zgyFiIiZ3sJQr0RQx5frLa6k7SOKRo3oMFNR5t62h2rttKXEOKFqDCxtXNmmBokO2KKTlp3IdWuT2dYRNGKwEXEBCcL172G5FG0aIxC0kR9PBTVH1kkwQn+IqJnCE33EalVzT9GJQS1tAdD3CKicJYFrxqx7W2ejCEdZy1FiC5tZxHhLJKOZaRdQJAyV/YAvDliySALHxmxR4Hqe2iwvaOR/CEuZYJFSgYhVbZRkA8KGdEktrqnqra90NndCdkt77fjIHIhexOrfO6O3bbbOj/rqu5IptgyR3sU93QbOYhquZK4MCDp0Ina/PLsu5JvbCTRaapUdUmIV/RzoMdsk/0hWRNdAvKOmvqlN0drsJbJf1P4YsQ5lGrJeuosiOUgbOC8cto3LfOXTdVd7BqZsQKbse+0jUL6WPcesqs4MNSUTQAxGjwFiC8m3yzmqwHJBWYKBJ9WNqW/dHkpU/osch1Yj5RJfXPfSEe/2UPsN490NPfZG5CKyJmcV5ayHyzy7BMqsXfuHhGK/cjAIeSpR92gehR55D8TcQhDEKJwytBJ4fr4NULvrEM8NszfJPyxDoHYAQ1oPCWmIX4gifmDS/DV2DKeb25FHWr76yEG7/9L4YFPeiQQ4/8LkgJ8Et+NncTCsYqzXAEXa7CWdPZzGWdlyV+vST0JanfPvwAAAABJRU5ErkJggg==';
|
||||
}
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\DataCollector;
|
||||
|
||||
use Drupal\webprofiler\DrupalDataCollectorInterface;
|
||||
use Drupal\Core\Routing\RouteProviderInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
||||
|
||||
/**
|
||||
* Provides a data collector which shows all available routes.
|
||||
*/
|
||||
class RoutingDataCollector extends DataCollector implements DrupalDataCollectorInterface {
|
||||
|
||||
use StringTranslationTrait, DrupalDataCollectorTrait;
|
||||
|
||||
/**
|
||||
* The route profiler.
|
||||
*
|
||||
* @var \Drupal\Core\Routing\RouteProviderInterface
|
||||
*/
|
||||
protected $routeProvider;
|
||||
|
||||
/**
|
||||
* Constructs a new RoutingDataCollector.
|
||||
*
|
||||
* @param \Drupal\Core\Routing\RouteProviderInterface $routeProvider
|
||||
* The route provider.
|
||||
*/
|
||||
public function __construct(RouteProviderInterface $routeProvider) {
|
||||
$this->routeProvider = $routeProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Exception $exception = NULL) {
|
||||
$this->data['routing'] = [];
|
||||
foreach ($this->routeProvider->getAllRoutes() as $route_name => $route) {
|
||||
// @TODO Find a better visual representation.
|
||||
$this->data['routing'][] = [
|
||||
'name' => $route_name,
|
||||
'path' => $route->getPath(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getRoutesCount() {
|
||||
return count($this->routing());
|
||||
}
|
||||
|
||||
/**
|
||||
* Twig callback for displaying the routes.
|
||||
*/
|
||||
public function routing() {
|
||||
return $this->data['routing'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle() {
|
||||
return $this->t('Routing');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName() {
|
||||
return 'routing';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPanelSummary() {
|
||||
return $this->t('Defined routes: @route', ['@route' => $this->getRoutesCount()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIcon() {
|
||||
return 'iVBORw0KGgoAAAANSUhEUgAAABUAAAAcCAYAAACOGPReAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAPxJREFUeNrkVsERREAQ7D0koXwlIAISkIGXvwAkoJSfHITg5SMUVR4eMuCxZ1xxV+5uuTL30lV8bPX09PSuFVJKcOOGP+DipLrrusoFdV1Lz/NgmiaKosC0XrAopYT0fc/f/jAMa41Pj23bkrpi9bRpGmRZ9lRKFSzLkl9UHMI4jijLcuaaSZMkQdd1fNMn5r0EHIFhGEjTlDenYRjCcZyHUnqRwXEcz77sYepM+Z1yTOEXZEFVVYcUba2iItsNcbr9IAiw5HMd1CJZtU1VpG3bIooi3gPF933keY43pb9gb1Bskdrap58luMjvRNO04wcK18RfIa59mbgLMAASuWsKAyoEhgAAAABJRU5ErkJggg==';
|
||||
}
|
||||
}
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
<?php
|
||||
|
||||
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\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
||||
|
||||
/**
|
||||
* Class ServicesDataCollector
|
||||
*/
|
||||
class ServicesDataCollector extends DataCollector implements DrupalDataCollectorInterface {
|
||||
|
||||
use StringTranslationTrait, DrupalDataCollectorTrait;
|
||||
|
||||
/**
|
||||
* @var \Symfony\Component\DependencyInjection\IntrospectableContainerInterface
|
||||
* $container
|
||||
*/
|
||||
private $container;
|
||||
|
||||
/**
|
||||
* @param IntrospectableContainerInterface $container
|
||||
*/
|
||||
public function __construct(IntrospectableContainerInterface $container) {
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Exception $exception = NULL) {
|
||||
if ($this->getServicesCount()) {
|
||||
|
||||
$tracedData = [];
|
||||
if ($this->container instanceof TraceableContainer) {
|
||||
$tracedData = $this->container->getTracedData();
|
||||
}
|
||||
|
||||
foreach (array_keys($this->getServices()) as $id) {
|
||||
$this->data['services'][$id]['initialized'] = ($this->container->initialized($id)) ? TRUE : FALSE;
|
||||
$this->data['services'][$id]['time'] = isset($tracedData[$id]) ? $tracedData[$id] : NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $services
|
||||
*/
|
||||
public function setServices($services) {
|
||||
$this->data['services'] = $services;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getServices() {
|
||||
return $this->data['services'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getServicesCount() {
|
||||
return count($this->getServices());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getInitializedServices() {
|
||||
return array_filter($this->getServices(), function($item) {
|
||||
return $item['initialized'];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getInitializedServicesCount() {
|
||||
return count($this->getInitializedServices());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getInitializedServicesWithoutWebprofiler() {
|
||||
return array_filter($this->getInitializedServices(), function($item) {
|
||||
return strpos($item['value']['id'], 'webprofiler') !== 0;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getInitializedServicesWithoutWebprofilerCount() {
|
||||
return count($this->getInitializedServicesWithoutWebprofiler());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName() {
|
||||
return 'services';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle() {
|
||||
return $this->t('Services');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPanelSummary() {
|
||||
return $this->t('Initialized: @count', [
|
||||
'@count' => $this->getInitializedServicesCount(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIcon() {
|
||||
return 'iVBORw0KGgoAAAANSUhEUgAAABUAAAAcCAYAAACOGPReAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQVJREFUeNrkVe0NgjAQBeMAdYO6AWxQNtANGEFHcALZANyADegGsIFsIBvgu6Q/LtWmxdTEjyYvd6Hw8t5de6TzPCex1yp5w/pz0rVrQymVIXSAACqt9TGG0p0hpHWIZb9lebWENOXn1FgWbL8GJHACNHs+ohyjlxSEZPEcKGYC6SbEvljgUHzEOR3IXiiB6YOTlLqdo1Y54tZHDLIauCHtETtn962P6EUVqhhi0gelIJEEk1MjMg9Py9xol/0SuBqFva/DULY3ZSqQF767v8TyZKv83tFXWVaEufsUG+DCr2nwQLGOlGQNizZPy3fMU16K5uV5+qQEpFTC+hCN9Pd/0XcBBgBxwVqjDkAznAAAAABJRU5ErkJggg==';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getData() {
|
||||
$data = $this->data;
|
||||
|
||||
$http_middleware = array_filter($data['services'], function($service) {
|
||||
return isset($service['value']['tags']['http_middleware']);
|
||||
});
|
||||
|
||||
foreach ($http_middleware as &$service) {
|
||||
$service['value']['handle_method'] = $this->getMethodData($service['value']['class'], 'handle');
|
||||
}
|
||||
|
||||
uasort($http_middleware, function ($a, $b) {
|
||||
$va = $a['value']['tags']['http_middleware'][0]['priority'];
|
||||
$vb = $b['value']['tags']['http_middleware'][0]['priority'];
|
||||
|
||||
if ($va == $vb) {
|
||||
return 0;
|
||||
}
|
||||
return ($va > $vb) ? -1 : 1;
|
||||
});
|
||||
|
||||
$data['http_middleware'] = $http_middleware;
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\DataCollector;
|
||||
|
||||
use Drupal\webprofiler\DrupalDataCollectorInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
||||
|
||||
/**
|
||||
* Provides a data collector to get all requested state values.
|
||||
*/
|
||||
class StateDataCollector extends DataCollector implements DrupalDataCollectorInterface {
|
||||
|
||||
use StringTranslationTrait, DrupalDataCollectorTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Exception $exception = NULL) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
*/
|
||||
public function addState($key) {
|
||||
$this->data['state_get'][$key] = isset($this->data['state_get'][$key]) ? $this->data['state_get'][$key] + 1 : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Twig callback to show all requested state keys.
|
||||
*/
|
||||
public function getStateKeysCount() {
|
||||
return count($this->data['state_get']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle() {
|
||||
return $this->t('State');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName() {
|
||||
return 'state';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPanelSummary() {
|
||||
return $this->t('Total: @variables', ['@variables' => $this->getStateKeysCount()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIcon() {
|
||||
return 'iVBORw0KGgoAAAANSUhEUgAAABUAAAAcCAYAAACOGPReAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNS4xIE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2Njc3QTVERTkxNkMxMUUzQjA3OUEzQTNEMUVGMjVDOCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo2Njc3QTVERjkxNkMxMUUzQjA3OUEzQTNEMUVGMjVDOCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjY2NzdBNURDOTE2QzExRTNCMDc5QTNBM0QxRUYyNUM4IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjY2NzdBNUREOTE2QzExRTNCMDc5QTNBM0QxRUYyNUM4Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+tji6wwAAAUFJREFUeNpi/P//PwO1ARMDDcDQMZSBgZExE4h/AvF/KuA/QFzKCCS+TZ40iVNeXp5i9718+ZIhNS3tL5OoqChVDAQBcXFxBqBZzDQJU0Z2Do7/FubmVDPwzNmzwymdhoSEgDEM8PDwgPkJCQk49bDgMxCkEWQAyKAXL14wXLhwgWHChAlguTVr1pBnKMiwgoICsEEgC+7cuQPGHR0deH3HzMLC0iAjI4NV8tSpUwzv3r1jYGNjY3BycoJbgg88e/6cuIg6cuQImAZ5n2qx7+HhAaZtbGzArqXYUJCBIAwKR1jMq6ioMBgYGJAeURUVFWCNsHAERRAsKYFofJGF01CQIaBktGPHDjANAjU1NWBXg8IWX/gOobz/8+fP7x8/fqSKYV++fGH4+vXrX1B1EgXET6hUnTwH4jTG0Xqf6gAgwABnDJn0eW/JMwAAAABJRU5ErkJggg==';
|
||||
}
|
||||
}
|
||||
+257
@@ -0,0 +1,257 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\DataCollector;
|
||||
|
||||
use Drupal\Core\Theme\ThemeManagerInterface;
|
||||
use Drupal\Core\Theme\ThemeNegotiatorInterface;
|
||||
use Drupal\webprofiler\DrupalDataCollectorInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\webprofiler\Theme\ThemeNegotiatorWrapper;
|
||||
use Drupal\webprofiler\Twig\Dumper\HtmlDumper;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
||||
use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface;
|
||||
|
||||
/**
|
||||
* Class ThemeDataCollector
|
||||
*/
|
||||
class ThemeDataCollector extends DataCollector implements DrupalDataCollectorInterface, LateDataCollectorInterface {
|
||||
|
||||
use StringTranslationTrait, DrupalDataCollectorTrait;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Theme\ThemeManagerInterface
|
||||
*/
|
||||
private $themeManager;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Theme\ThemeNegotiatorInterface
|
||||
*/
|
||||
private $themeNegotiator;
|
||||
|
||||
/**
|
||||
* @var \Twig_Profiler_Profile
|
||||
*/
|
||||
private $profile;
|
||||
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
private $computed;
|
||||
|
||||
/**
|
||||
* @param \Drupal\Core\Theme\ThemeManagerInterface $themeManager
|
||||
* @param \Drupal\Core\Theme\ThemeNegotiatorInterface $themeNegotiator
|
||||
* @param \Twig_Profiler_Profile $profile
|
||||
*/
|
||||
public function __construct(ThemeManagerInterface $themeManager, ThemeNegotiatorInterface $themeNegotiator, \Twig_Profiler_Profile $profile) {
|
||||
$this->themeManager = $themeManager;
|
||||
$this->themeNegotiator = $themeNegotiator;
|
||||
$this->profile = $profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Exception $exception = NULL) {
|
||||
$activeTheme = $this->themeManager->getActiveTheme();
|
||||
|
||||
$this->data['activeTheme'] = [
|
||||
'name' => $activeTheme->getName(),
|
||||
'path' => $activeTheme->getPath(),
|
||||
'engine' => $activeTheme->getEngine(),
|
||||
'owner' => $activeTheme->getOwner(),
|
||||
'baseThemes' => $activeTheme->getBaseThemes(),
|
||||
'extension' => $activeTheme->getExtension(),
|
||||
'styleSheetsRemove' => $activeTheme->getStyleSheetsRemove(),
|
||||
'libraries' => $activeTheme->getLibraries(),
|
||||
'regions' => $activeTheme->getRegions(),
|
||||
];
|
||||
|
||||
if ($this->themeNegotiator instanceof ThemeNegotiatorWrapper) {
|
||||
$this->data['negotiator'] = [
|
||||
'class' => $this->getMethodData($this->themeNegotiator->getNegotiator(), 'determineActiveTheme'),
|
||||
'id' => $this->themeNegotiator->getNegotiator()->_serviceId,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function lateCollect() {
|
||||
$this->data['twig'] = serialize($this->profile);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getActiveTheme() {
|
||||
return $this->data['activeTheme'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getThemeNegotiator() {
|
||||
return $this->data['negotiator'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getTime() {
|
||||
return $this->getProfile()->getDuration() * 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getTemplateCount() {
|
||||
return $this->getComputedData('template_count');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getTemplates() {
|
||||
return $this->getComputedData('templates');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getBlockCount() {
|
||||
return $this->getComputedData('block_count');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getMacroCount() {
|
||||
return $this->getComputedData('macro_count');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Twig_Markup
|
||||
*/
|
||||
public function getHtmlCallGraph() {
|
||||
$dumper = new HtmlDumper();
|
||||
|
||||
return new \Twig_Markup($dumper->dump($this->getProfile()), 'UTF-8');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName() {
|
||||
return 'theme';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle() {
|
||||
return $this->t('Theme');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPanelSummary() {
|
||||
return $this->t('Name: @name', ['@name' => $this->getActiveTheme()['name']]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIcon() {
|
||||
return 'iVBORw0KGgoAAAANSUhEUgAAABUAAAAcCAYAAACOGPReAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAlVJREFUeNrMlk1IVFEUx53K6AOjUBQri6SihQiRziaRQegDlKJFBCpSiZtoE4hWtFFRwcCVBqEWfaAE7SoXajXLQoRcFAUSaBgUJQxKYUrT78D/wevx3psaE7rw49x377ln7vl4500kmUxm/OuxJmMVxrqwzVgsth5xHKogCnthCd7BU7gdj8envOciQe5j8CyiFQq0tAgDsA+OaG0eLmF4INQoxrYg+iEXhqEGiqGBw/3SGUeU6MgC61mBMUU5GzEKJ6ES5S5knbbXui/jmv8MjCkGMxEPFbsFyAeL1y6pXEenSO4fctlIhCXqqtmGj5ADcblZoX1z8aLn/Es475soDhcyfwt3LPDK8jPYFlIcZrCCEH0LqlP7NXN/CCUL/CvmkynKcczPoNv9Y5LXuHVC2Y6mMFoetOG4/0kl9LfDquMyN076ue/I11ANHSqVH0pgLUz7GK2HjUHuf1DGm/jVYZVYJeILz5163oy46Tn/GL5r396yc+hXOzcckiyVQp7CsVNvmI2DHoODcMFcR6eZ+RO4747pJuYjcFilssMM6vB7+Or8IGMWWjDWx7ntzHvglCWZtfbf3n0UtiJ6FVO/8QbuQh+H59C37nUD9sAj1k6ENZQylctuJWsGJswDDibY32C3gitKsCUwyt7nlK0vpMc2Wh/Q4zIcxeDztDu/QjSoPmujzWswZef3GLRXuV2xPa3u/yDwjfoDgxE1nP1aquOG91b64StWlp0xtaKvKbfMUS1maukWvEj7a6pxBg6oL1iddnsbSFox/S/+TKyK0V8CDABrCdI/1oTqiQAAAABJRU5ErkJggg==';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getData() {
|
||||
$data = $this->data;
|
||||
|
||||
$data['twig'] = [
|
||||
'callgraph' => (string) $this->getHtmlCallGraph(),
|
||||
'render_time' => $this->getTime(),
|
||||
'template_count' => $this->getTemplateCount(),
|
||||
'templates' => $this->getTemplates(),
|
||||
'block_count' => $this->getBlockCount(),
|
||||
'macro_count' => $this->getMacroCount(),
|
||||
];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed|\Twig_Profiler_Profile
|
||||
*/
|
||||
private function getProfile() {
|
||||
if (NULL === $this->profile) {
|
||||
$this->profile = unserialize($this->data['twig']);
|
||||
}
|
||||
|
||||
return $this->profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $index
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
private function getComputedData($index) {
|
||||
if (NULL === $this->computed) {
|
||||
$this->computed = $this->computeData($this->getProfile());
|
||||
}
|
||||
|
||||
return $this->computed[$index];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Twig_Profiler_Profile $profile
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function computeData(\Twig_Profiler_Profile $profile) {
|
||||
$data = [
|
||||
'template_count' => 0,
|
||||
'block_count' => 0,
|
||||
'macro_count' => 0,
|
||||
];
|
||||
|
||||
$templates = [];
|
||||
foreach ($profile as $p) {
|
||||
$d = $this->computeData($p);
|
||||
|
||||
$data['template_count'] += ($p->isTemplate() ? 1 : 0) + $d['template_count'];
|
||||
$data['block_count'] += ($p->isBlock() ? 1 : 0) + $d['block_count'];
|
||||
$data['macro_count'] += ($p->isMacro() ? 1 : 0) + $d['macro_count'];
|
||||
|
||||
if ($p->isTemplate()) {
|
||||
if (!isset($templates[$p->getTemplate()])) {
|
||||
$templates[$p->getTemplate()] = 1;
|
||||
}
|
||||
else {
|
||||
$templates[$p->getTemplate()]++;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($d['templates'] as $template => $count) {
|
||||
if (!isset($templates[$template])) {
|
||||
$templates[$template] = $count;
|
||||
}
|
||||
else {
|
||||
$templates[$template] += $count;
|
||||
}
|
||||
}
|
||||
}
|
||||
$data['templates'] = $templates;
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\DataCollector;
|
||||
|
||||
use Drupal\webprofiler\DrupalDataCollectorInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\DataCollector\TimeDataCollector as BaseTimeDataCollector;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
use Symfony\Component\Stopwatch\StopwatchEvent;
|
||||
|
||||
/**
|
||||
* Class TimeDataCollector.
|
||||
*/
|
||||
class TimeDataCollector extends BaseTimeDataCollector implements DrupalDataCollectorInterface {
|
||||
|
||||
use StringTranslationTrait, DrupalDataCollectorTrait;
|
||||
|
||||
/**
|
||||
* @param \Symfony\Component\HttpKernel\KernelInterface $kernel
|
||||
* @param $stopwatch
|
||||
*/
|
||||
public function __construct(KernelInterface $kernel = NULL, $stopwatch = NULL) {
|
||||
parent::__construct($kernel, $stopwatch);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Exception $exception = NULL) {
|
||||
parent::collect($request, $response, $exception);
|
||||
|
||||
$this->data['memory_limit'] = $this->convertToBytes(ini_get('memory_limit'));
|
||||
$this->updateMemoryUsage();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function lateCollect() {
|
||||
parent::lateCollect();
|
||||
|
||||
$this->updateMemoryUsage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the memory.
|
||||
*
|
||||
* @return int
|
||||
* The memory
|
||||
*/
|
||||
public function getMemory() {
|
||||
return $this->data['memory'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the PHP memory limit.
|
||||
*
|
||||
* @return int
|
||||
* The memory limit
|
||||
*/
|
||||
public function getMemoryLimit() {
|
||||
return $this->data['memory_limit'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the memory usage data.
|
||||
*/
|
||||
public function updateMemoryUsage() {
|
||||
$this->data['memory'] = memory_get_peak_usage(TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle() {
|
||||
return $this->t('Timeline');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPanelSummary() {
|
||||
return $this->t('Duration: @duration', ['@duration' => sprintf('%.0f ms', $this->getDuration())]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIcon() {
|
||||
return 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAcCAYAAABoMT8aAAABqUlEQVR42t2Vv0sCYRyHX9OmEhsMx/YKGlwLQ69DTEUSBJEQEy5J3FRc/BsuiFqEIIcQIRo6ysUhoaBBWhoaGoJwiMJLglRKrs8bXgienmkQdPDAwX2f57j3fhFJkkbiPwTK5bIiFoul3kmPud8MqKMewDXpwuGww+12n9hsNhFnlijYf/Z4PDmO45Yxo+10ZFGTyWRMEItU6AdCx7lczkgd6n7J2Wx2xm63P6jJMk6n80YQBBN1aUDv9XqvlAbbm2LE7/cLODRB0un0VveAeoDC8/waCQQC18MGQqHQOcEKvw8bcLlcL6TfYnVtCrGRAlartUUYhmn1jKg/E3USjUYfhw3E4/F7ks/nz4YNFIvFQ/ogbUYikdefyqlU6gnuOg2YK5XKvs/n+xhUDgaDTVEUt+HO04ABOBA5isViDTU5kUi81Wq1AzhWMEkDGmAEq2C3UCjcYXGauDvfEsuyUjKZbJRKpVvM8IABU9SVX+cxYABmwIE9cFqtVi9xtgvsC2AHbIAFoKey0gdlHEyDObAEWLACFsEsMALdIJ80+dK0bTS95v7+v/AJnis0eO906QwAAAAASUVORK5CYII=';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLibraries() {
|
||||
return [
|
||||
'webprofiler/timeline',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDrupalSettings() {
|
||||
/** @var StopwatchEvent[] $collectedEvents */
|
||||
$collectedEvents = $this->getEvents();
|
||||
|
||||
if (!empty($collectedEvents)) {
|
||||
$sectionPeriods = $collectedEvents['__section__']->getPeriods();
|
||||
$endTime = end($sectionPeriods)->getEndTime();
|
||||
$events = [];
|
||||
|
||||
foreach ($collectedEvents as $key => $collectedEvent) {
|
||||
if ('__section__' != $key) {
|
||||
$periods = [];
|
||||
foreach ($collectedEvent->getPeriods() as $period) {
|
||||
$periods[] = [
|
||||
'start' => sprintf("%F", $period->getStartTime()),
|
||||
'end' => sprintf("%F", $period->getEndTime()),
|
||||
];
|
||||
}
|
||||
|
||||
$events[] = [
|
||||
"name" => $key,
|
||||
"category" => $collectedEvent->getCategory(),
|
||||
"origin" => sprintf("%F", $collectedEvent->getOrigin()),
|
||||
"starttime" => sprintf("%F", $collectedEvent->getStartTime()),
|
||||
"endtime" => sprintf("%F", $collectedEvent->getEndTime()),
|
||||
"duration" => sprintf("%F", $collectedEvent->getDuration()),
|
||||
"memory" => sprintf("%.1F", $collectedEvent->getMemory() / 1024 / 1024),
|
||||
"periods" => $periods,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return ['time' => ['events' => $events, 'endtime' => $endTime]];
|
||||
}
|
||||
else {
|
||||
return ['time' => ['events' => [], 'endtime' => 0]];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getData() {
|
||||
$data = $this->data;
|
||||
|
||||
$data['duration'] = $this->getDuration();
|
||||
$data['initTime'] = $this->getInitTime();
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\DataCollector;
|
||||
|
||||
use Drupal\Core\Routing\UrlGeneratorInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\Core\StringTranslation\TranslationInterface;
|
||||
use Drupal\webprofiler\DrupalDataCollectorInterface;
|
||||
use Drupal\webprofiler\StringTranslation\TranslationManagerWrapper;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
||||
|
||||
/**
|
||||
* Class TranslationsDataCollector
|
||||
*/
|
||||
class TranslationsDataCollector extends DataCollector implements DrupalDataCollectorInterface {
|
||||
|
||||
use StringTranslationTrait, DrupalDataCollectorTrait;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\StringTranslation\TranslationInterface
|
||||
*/
|
||||
private $translation;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Routing\UrlGeneratorInterface
|
||||
*/
|
||||
private $urlGenerator;
|
||||
|
||||
/**
|
||||
* @param \Drupal\Core\StringTranslation\TranslationInterface $translation
|
||||
* @param \Drupal\Core\Routing\UrlGeneratorInterface $urlGenerator
|
||||
*/
|
||||
public function __construct(TranslationInterface $translation, UrlGeneratorInterface $urlGenerator) {
|
||||
$this->translation = $translation;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
|
||||
$this->data['translations']['translated'] = [];
|
||||
$this->data['translations']['untranslated'] = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Exception $exception = NULL) {
|
||||
if($this->translation instanceof TranslationManagerWrapper) {
|
||||
/** \Drupal\webprofiler\StringTranslation\TranslationManagerWrapper $this->translation */
|
||||
$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');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getTranslatedCount() {
|
||||
return count($this->data['translations']['translated']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getUntranslatedCount() {
|
||||
return count($this->data['translations']['untranslated']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName() {
|
||||
return 'translations';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle() {
|
||||
return $this->t('Translations');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPanelSummary() {
|
||||
return $this->t('Translated: @translated, untranslated: @untranslated', [
|
||||
'@translated' => $this->getTranslatedCount(),
|
||||
'@untranslated' => $this->getUntranslatedCount()
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIcon() {
|
||||
return 'iVBORw0KGgoAAAANSUhEUgAAABUAAAAcCAYAAACOGPReAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAgpJREFUeNrUVrFuwjAQPUcZiAQDQ6WEbgwwMCJVqhjgBxhYGSvEDzBkZYSBH+ADoFJHlg4MdIMKxiLBXKAbUmEm9bPiyAREEgRDTzqZ2Mfzu7tnJ8xxHLq1aXQHuwsos2078p9arVYg0yL3CXcnwCdubKDp3F+5myFin9xYK0xNzQiZm1c3KpVKUb1ep2QyebvuFwoFSqfTlM/nbweay+VoOBxeDar7JwC03W49UO5fs9nsZTwef8qYUqkUjSmAYrGYqKlhGFSpVH45oBFFdkdM0RjUst1uC7Z45ofjWdO0t8Ph8CDjyuUyZTIZ6na7tNvtTmSn+2s5n88FIMwdPzhgUWYBA2A2mxVjIpEQarEsiwaDAS2XSxPH9OI1xVNnbmo0Go28eTDkAOL3YrGg/X4v1tfr9WmjLplsULVaFawbjYbHkjfzqPsr7o9RwJE2HOkifbAGS1ljdL/G/ScsWK/XE+NmsxGMkW6/3xfgWMMIpu9hLgkEN5tNDwClgAoADjAYRszr8m7kD2gGU5uh1hEjmoGUUU/oGLVEXaVhA2yoKYAwRwFx/Ezj8bjHEIYaSgVgDptNp1NiePG5QCfS4qyZXANop9MR7JANWAFA6hWgWEcmqqTYOWBVl0jZf0ViU+hUNk0AyVf0ObYK0+8IslupF8qlkxVWdoipsaCPiaBr7uwr+t98ofwJMADuIP9IDjFbLwAAAABJRU5ErkJggg==';
|
||||
}
|
||||
}
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\DataCollector;
|
||||
|
||||
use Drupal\Core\Authentication\AuthenticationCollectorInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\webprofiler\DrupalDataCollectorInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
||||
|
||||
/**
|
||||
* Class UserDataCollector
|
||||
*/
|
||||
class UserDataCollector extends DataCollector implements DrupalDataCollectorInterface {
|
||||
|
||||
use StringTranslationTrait, DrupalDataCollectorTrait;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Session\AccountInterface
|
||||
*/
|
||||
private $currentUser;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
private $entityManager;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||
*/
|
||||
private $configFactory;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Authentication\AuthenticationCollectorInterface
|
||||
*/
|
||||
private $providerCollector;
|
||||
|
||||
/**
|
||||
* @param \Drupal\Core\Session\AccountInterface $currentUser
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entityManager
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
|
||||
* @param \Drupal\Core\Authentication\AuthenticationCollectorInterface $providerCollector
|
||||
*/
|
||||
public function __construct(AccountInterface $currentUser, EntityManagerInterface $entityManager, ConfigFactoryInterface $configFactory, AuthenticationCollectorInterface $providerCollector) {
|
||||
$this->currentUser = $currentUser;
|
||||
$this->entityManager = $entityManager;
|
||||
$this->configFactory = $configFactory;
|
||||
$this->providerCollector = $providerCollector;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Exception $exception = NULL) {
|
||||
$this->data['name'] = $this->currentUser->getDisplayName();
|
||||
$this->data['authenticated'] = $this->currentUser->isAuthenticated();
|
||||
|
||||
$this->data['roles'] = [];
|
||||
$storage = $this->entityManager->getStorage('user_role');
|
||||
foreach ($this->currentUser->getRoles() as $role) {
|
||||
$entity = $storage->load($role);
|
||||
$this->data['roles'][] = $entity->label();
|
||||
}
|
||||
|
||||
foreach ($this->providerCollector->getSortedProviders() as $provider_id => $provider) {
|
||||
if ($provider->applies($request)) {
|
||||
$this->data['provider'] = $provider_id;
|
||||
}
|
||||
}
|
||||
|
||||
$this->data['anonymous'] = $this->configFactory->get('user.settings')
|
||||
->get('anonymous');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Drupal\Core\Session\AccountInterface
|
||||
*/
|
||||
public function getUserName() {
|
||||
return $this->data['name'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function getAuthenticated() {
|
||||
return $this->data['authenticated'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getRoles() {
|
||||
return $this->data['roles'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getProvider() {
|
||||
return $this->data['provider'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getAnonymous() {
|
||||
return $this->data['anonymous'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName() {
|
||||
return 'user';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle() {
|
||||
return $this->t('User');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPanelSummary() {
|
||||
return $this->getUserName();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIcon() {
|
||||
return 'iVBORw0KGgoAAAANSUhEUgAAABgAAAAcCAYAAAB75n/uAAAC70lEQVR42u2V3UtTYRzHu+mFwCwK+gO6CEryPlg7yiYx50vDqUwjFIZDSYUk2ZTmCysHvg9ZVggOQZiRScsR4VwXTjEwdKZWk8o6gd5UOt0mbev7g/PAkLONIOkiBx+25/v89vuc85zn2Q5Fo9F95UDwnwhS5HK5TyqVRv8m1JN6k+AiC+fn54cwbgFNIrTQ/J9IqDcJJDGBHsgDgYBSq9W6ysvLPf39/SSUUU7zsQ1yc3MjmN90OBzfRkZG1umzQqGIxPSTkIBjgdDkaGNjoza2kcFgUCE/QvMsq6io2PV6vQu1tbV8Xl7etkql2qqvr/+MbDE/Pz8s9OP2Cjhwwmw29+4R3Kec1WZnZ4fn5uamc3Jyttra2qbH8ero6JgdHh5+CvFHq9X6JZHgzODgoCVW0NPTY0N+ltU2Nzdv4GqXsYSrPp+vDw80aLFYxru6uhyQ/rDb7a8TCVJDodB1jUazTVlxcXGQ5/mbyE+z2u7u7veY38BVT3Z2djopm5qa6isrK/tQWVn5qb29fSGR4DC4PDAwMEsZHuArjGnyGKutq6v7ajQaF6urq9/MzMz0QuSemJiwQDwGkR0POhhXgILjNTU1TaWlpTxlOp1uyWQyaUjMajMzM8Nut/tJQUHBOpZppbCwkM/KytrBznuL9xDVxBMo8KXHYnu6qKjIivmrbIy67x6Px4Yd58W672ApfzY0NCyNjo7OZmRkiAv8fr+O47iwmABXtoXaG3uykF6vX7bZbF6cgZWqqiqezYkKcNtmjO+CF2AyhufgjsvlMiU7vXEF+4C4ALf9CwdrlVAqlcFkTdRqdQSHLUDgBEeSCrArAsiGwENs0XfJBE6ncxm1D8Aj/B6tigkkJSUlmxSwLYhMDeRsyyUCd+lHrWxtbe2aTCbbZTn1ZD92F0Cr8GBfgnsgDZwDt8EzMBmHMXBLqD0PDMAh9Gql3iRIESQSIAXp4CRIBZeEjIvDFZAm1J4C6UK9ROiZcvCn/+8FvwHtDdJEaRY+oQAAAABJRU5ErkJggg==';
|
||||
}
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\DataCollector;
|
||||
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\webprofiler\DrupalDataCollectorInterface;
|
||||
use Drupal\webprofiler\Views\TraceableViewExecutable;
|
||||
use Drupal\webprofiler\Views\ViewExecutableFactoryWrapper;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
||||
|
||||
/**
|
||||
* Collects data about rendered views.
|
||||
*/
|
||||
class ViewsDataCollector extends DataCollector implements DrupalDataCollectorInterface {
|
||||
|
||||
use StringTranslationTrait, DrupalDataCollectorTrait;
|
||||
|
||||
/**
|
||||
* @var \Drupal\webprofiler\Views\ViewExecutableFactoryWrapper
|
||||
*/
|
||||
private $view_executable_factory;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
private $entityManager;
|
||||
|
||||
/**
|
||||
* @param ViewExecutableFactoryWrapper $view_executable_factory
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entityManager
|
||||
*/
|
||||
public function __construct(ViewExecutableFactoryWrapper $view_executable_factory, EntityManagerInterface $entityManager) {
|
||||
$this->entityManager = $entityManager;
|
||||
$this->view_executable_factory = $view_executable_factory;
|
||||
|
||||
$this->data['views'] = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Exception $exception = NULL) {
|
||||
$views = $this->view_executable_factory->getViews();
|
||||
$storage = $this->entityManager->getStorage('view');
|
||||
|
||||
/** @var TraceableViewExecutable $view */
|
||||
foreach ($views as $view) {
|
||||
if ($view->executed) {
|
||||
$data = [
|
||||
'id' => $view->storage->id(),
|
||||
'current_display' => $view->current_display,
|
||||
'build_time' => $view->getBuildTime(),
|
||||
'execute_time' => $view->getExecuteTime(),
|
||||
'render_time' => $view->getRenderTime(),
|
||||
];
|
||||
|
||||
$entity = $storage->load($view->storage->id());
|
||||
if ($entity->hasLinkTemplate('edit-display-form')) {
|
||||
$route = $entity->toUrl('edit-display-form');
|
||||
$route->setRouteParameter('display_id', $view->current_display);
|
||||
$data['route'] = $route->toString();
|
||||
}
|
||||
|
||||
$this->data['views'][] = $data;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: also use those data.
|
||||
// $loaded = $this->entityManager->getLoaded('view');
|
||||
//
|
||||
// if ($loaded) {
|
||||
// /** @var \Drupal\webprofiler\Entity\EntityStorageDecorator $views */
|
||||
// foreach ($loaded->getEntities() as $views) {
|
||||
// $this->data['views'][] = array(
|
||||
// 'id' => $views->get('id'),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getViewsCount() {
|
||||
return count($this->data['views']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName() {
|
||||
return 'views';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle() {
|
||||
return $this->t('Views');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPanelSummary() {
|
||||
return $this->t('Total: @count', ['@count' => $this->getViewsCount()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIcon() {
|
||||
return 'iVBORw0KGgoAAAANSUhEUgAAABUAAAAcCAYAAACOGPReAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2hpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowNDgwMTE3NDA3MjA2ODExOEY2MkNCNjI0NDY3NzkwRCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDozNEVFREM2NkQ4MUMxMUUzQkMwRUNBMkQwMzE4QjVBMyIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozNEVFREM2NUQ4MUMxMUUzQkMwRUNBMkQwMzE4QjVBMyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1LjEgTWFjaW50b3NoIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MDQ4MDExNzQwNzIwNjgxMThGNjJDQjYyNDQ2Nzc5MEQiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDQ4MDExNzQwNzIwNjgxMThGNjJDQjYyNDQ2Nzc5MEQiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz6vqYfFAAAAXUlEQVR42mL8//8/A7UBEwMNwKih1AcsIGLz5s1USwK+vr6MLMgcSg2EOW6IhSkycHR0BHth//79jMh8fACmlr4uRbcVnT8apqNhOhqmAxZR1CyoGUfrfaoDgAADAA4QNs9x67RnAAAAAElFTkSuQmCC';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler;
|
||||
|
||||
/**
|
||||
* Generic class Decorator.
|
||||
*/
|
||||
class Decorator {
|
||||
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
protected $object;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param object $object
|
||||
* The object to decorate.
|
||||
*/
|
||||
public function __construct($object) {
|
||||
$this->object = $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the original (i.e. non decorated) object.
|
||||
*
|
||||
* @return mixed
|
||||
* The original object.
|
||||
*/
|
||||
public function getOriginalObject() {
|
||||
$object = $this->object;
|
||||
while ($object instanceof Decorator) {
|
||||
$object = $object->getOriginalObject();
|
||||
}
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if $method is a PHP callable.
|
||||
*
|
||||
* @param string $method
|
||||
* The method name.
|
||||
* @param bool $checkSelf
|
||||
*
|
||||
* @return bool|mixed
|
||||
*/
|
||||
public function isCallable($method, $checkSelf = FALSE) {
|
||||
//Check the original object
|
||||
$object = $this->getOriginalObject();
|
||||
if (is_callable([$object, $method])) {
|
||||
return $object;
|
||||
}
|
||||
// Check Decorators.
|
||||
$object = $checkSelf ? $this : $this->object;
|
||||
while ($object instanceof Decorator) {
|
||||
if (is_callable([$object, $method])) {
|
||||
return $object;
|
||||
}
|
||||
$object = $this->object;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $method
|
||||
* @param $args
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __call($method, $args) {
|
||||
if ($object = $this->isCallable($method)) {
|
||||
return call_user_func_array([$object, $method], $args);
|
||||
}
|
||||
throw new \Exception(
|
||||
'Undefined method - ' . get_class($this->getOriginalObject()) . '::' . $method
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $property
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function __get($property) {
|
||||
$object = $this->getOriginalObject();
|
||||
if (property_exists($object, $property)) {
|
||||
return $object->$property;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\DependencyInjection;
|
||||
|
||||
use Drupal\Component\Utility\Timer;
|
||||
use Drupal\Core\DependencyInjection\Container;
|
||||
|
||||
/**
|
||||
* Extends the Drupal container class to trace service instantiations.
|
||||
*/
|
||||
class TraceableContainer extends Container {
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $tracedData;
|
||||
|
||||
/**
|
||||
* @var \Symfony\Component\Stopwatch\Stopwatch
|
||||
*/
|
||||
private $stopwatch = NULL;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $hasStopwatch = FALSE;
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
* @param int $invalidBehavior
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE) {
|
||||
if (!$this->stopwatch && $this->has('stopwatch')) {
|
||||
$this->stopwatch = parent::get('stopwatch');
|
||||
$this->stopwatch->openSection();
|
||||
$this->hasStopwatch = TRUE;
|
||||
}
|
||||
|
||||
if ('stopwatch' === $id) {
|
||||
return $this->stopwatch;
|
||||
}
|
||||
|
||||
Timer::start($id);
|
||||
if ($this->hasStopwatch) {
|
||||
$e = $this->stopwatch->start($id, 'service');
|
||||
}
|
||||
|
||||
$service = parent::get($id, $invalidBehavior);
|
||||
|
||||
$this->tracedData[$id] = Timer::stop($id);
|
||||
if ($this->hasStopwatch && $e->isStarted()) {
|
||||
$e->stop();
|
||||
}
|
||||
|
||||
return $service;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getTracedData() {
|
||||
return $this->tracedData;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler;
|
||||
|
||||
/**
|
||||
* Interface DrupalDataCollectorInterface.
|
||||
*/
|
||||
interface DrupalDataCollectorInterface {
|
||||
|
||||
/**
|
||||
* Returns the datacollector title.
|
||||
*
|
||||
* @return string
|
||||
* The datacollector title.
|
||||
*/
|
||||
public function getTitle();
|
||||
|
||||
/**
|
||||
* Returns the name of the collector.
|
||||
*
|
||||
* @return string
|
||||
* The collector name.
|
||||
*/
|
||||
public function getName();
|
||||
|
||||
/**
|
||||
* Returns the string used in vertical tab summary.
|
||||
*
|
||||
* @return string
|
||||
* The panel summary.
|
||||
*/
|
||||
public function getPanelSummary();
|
||||
|
||||
/**
|
||||
* Returns the collector icon in base64 format.
|
||||
*
|
||||
* @return string
|
||||
* The collector icon.
|
||||
*/
|
||||
public function getIcon();
|
||||
|
||||
/**
|
||||
* Returns true if this datacollector has a detail panel.
|
||||
*
|
||||
* @return bool
|
||||
* True if datacollector has a detail panel, false otherwise.
|
||||
*/
|
||||
public function hasPanel();
|
||||
|
||||
/**
|
||||
* Returns the libraries needed in detail panel.
|
||||
*
|
||||
* @return array
|
||||
* The render array for detail panel.
|
||||
*/
|
||||
public function getLibraries();
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getDrupalSettings();
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getData();
|
||||
}
|
||||
+214
@@ -0,0 +1,214 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Entity\Decorators\Config;
|
||||
|
||||
use Drupal\Core\Config\Config;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityInterface;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityStorageInterface;
|
||||
use Drupal\Core\Config\Entity\ImportableEntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityHandlerInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\webprofiler\Entity\EntityDecorator;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Class ConfigEntityStorageDecorator
|
||||
*/
|
||||
class ConfigEntityStorageDecorator extends EntityDecorator implements ConfigEntityStorageInterface, ImportableEntityStorageInterface, EntityHandlerInterface {
|
||||
|
||||
/**
|
||||
* @param ConfigEntityStorageInterface $controller
|
||||
*/
|
||||
public function __construct(ConfigEntityStorageInterface $controller) {
|
||||
parent::__construct($controller);
|
||||
|
||||
$this->entities = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function resetCache(array $ids = NULL) {
|
||||
$this->getOriginalObject()->resetCache($ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadMultiple(array $ids = NULL) {
|
||||
$entities = $this->getOriginalObject()->loadMultiple($ids);
|
||||
|
||||
$this->entities = array_merge($this->entities, $entities);
|
||||
|
||||
return $entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function load($id) {
|
||||
$entity = $this->getOriginalObject()->load($id);
|
||||
|
||||
$this->entities[$id] = $entity;
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadUnchanged($id) {
|
||||
return $this->getOriginalObject()->loadUnchanged($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadRevision($revision_id) {
|
||||
return $this->getOriginalObject()->loadRevision($revision_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteRevision($revision_id) {
|
||||
$this->getOriginalObject()->deleteRevision($revision_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadByProperties(array $values = []) {
|
||||
$entities = $this->getOriginalObject()->loadByProperties($values);
|
||||
|
||||
$this->entities = array_merge($this->entities, $entities);
|
||||
|
||||
return $entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function create(array $values = []) {
|
||||
return $this->getOriginalObject()->create($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete(array $entities) {
|
||||
$this->getOriginalObject()->delete($entities);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save(EntityInterface $entity) {
|
||||
return $this->getOriginalObject()->save($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQuery($conjunction = 'AND') {
|
||||
return $this->getOriginalObject()->getQuery($conjunction);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getEntityTypeId() {
|
||||
return $this->getOriginalObject()->getEntityTypeId();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getEntityType() {
|
||||
return $this->getOriginalObject()->getEntityType();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getIDFromConfigName($config_name, $config_prefix) {
|
||||
return substr($config_name, strlen($config_prefix . '.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createFromStorageRecord(array $values) {
|
||||
return $this->getOriginalObject()->createFromStorageRecord($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function updateFromStorageRecord(ConfigEntityInterface $entity, array $values) {
|
||||
return $this->getOriginalObject()
|
||||
->updateFromStorageRecord($entity, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAggregateQuery($conjunction = 'AND') {
|
||||
return $this->getOriginalObject()->getAggregateQuery($conjunction);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadOverrideFree($id) {
|
||||
return $this->getOriginalObject()->loadOverrideFree($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadMultipleOverrideFree(array $ids = NULL) {
|
||||
return $this->getOriginalObject()->loadMultipleOverrideFree($ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function importCreate($name, Config $new_config, Config $old_config) {
|
||||
$this->getOriginalObject()->importCreate($name, $new_config, $old_config);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function importUpdate($name, Config $new_config, Config $old_config) {
|
||||
$this->getOriginalObject()->importUpdate($name, $new_config, $old_config);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function importDelete($name, Config $new_config, Config $old_config) {
|
||||
$this->getOriginalObject()->importDelete($name, $new_config, $old_config);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function importRename($old_name, Config $new_config, Config $old_config) {
|
||||
$this->getOriginalObject()->importRename($old_name, $new_config, $old_config);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
|
||||
return new static(
|
||||
$entity_type,
|
||||
$container->get('config.factory'),
|
||||
$container->get('uuid'),
|
||||
$container->get('language_manager')
|
||||
);
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Entity\Decorators\Config;
|
||||
|
||||
use Drupal\user\RoleStorageInterface;
|
||||
|
||||
/**
|
||||
* Class RoleStorageDecorator
|
||||
*/
|
||||
class RoleStorageDecorator extends ConfigEntityStorageDecorator implements RoleStorageInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isPermissionInRoles($permission, array $rids) {
|
||||
return $this->getOriginalObject()->isPermissionInRoles($permission, $rids);
|
||||
}
|
||||
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Entity\Decorators\Config;
|
||||
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\shortcut\ShortcutSetInterface;
|
||||
use Drupal\shortcut\ShortcutSetStorageInterface;
|
||||
|
||||
/**
|
||||
* Class ShortcutSetStorageDecorator
|
||||
*/
|
||||
class ShortcutSetStorageDecorator extends ConfigEntityStorageDecorator implements ShortcutSetStorageInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function assignUser(ShortcutSetInterface $shortcut_set, $account) {
|
||||
// TODO: Implement assignUser() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function unassignUser($account) {
|
||||
// TODO: Implement unassignUser() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteAssignedShortcutSets(ShortcutSetInterface $entity) {
|
||||
// TODO: Implement deleteAssignedShortcutSets() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAssignedToUser($account) {
|
||||
// TODO: Implement getAssignedToUser() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function countAssignedUsers(ShortcutSetInterface $shortcut_set) {
|
||||
// TODO: Implement countAssignedUsers() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefaultSet(AccountInterface $account) {
|
||||
// TODO: Implement getDefaultSet() method.
|
||||
}
|
||||
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Entity\Decorators\Config;
|
||||
|
||||
use Drupal\taxonomy\VocabularyStorageInterface;
|
||||
|
||||
/**
|
||||
* Class EntityStorageDecorator
|
||||
*/
|
||||
class VocabularyStorageDecorator extends ConfigEntityStorageDecorator implements VocabularyStorageInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getToplevelTids($vids) {
|
||||
$this->getOriginalObject()->getToplevelTids($vids);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Entity;
|
||||
|
||||
use Drupal\webprofiler\Decorator;
|
||||
|
||||
/**
|
||||
* Class EntityDecorator
|
||||
*/
|
||||
class EntityDecorator extends Decorator {
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $entities;
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getEntities() {
|
||||
return $this->entities;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Entity;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Entity\EntityViewBuilderInterface;
|
||||
use Drupal\Core\Plugin\DefaultPluginManager;
|
||||
use Drupal\webprofiler\Entity\Decorators\Config\ConfigEntityStorageDecorator;
|
||||
use Drupal\webprofiler\Entity\Decorators\Config\RoleStorageDecorator;
|
||||
use Drupal\webprofiler\Entity\Decorators\Config\ShortcutSetStorageDecorator;
|
||||
use Drupal\webprofiler\Entity\Decorators\Config\VocabularyStorageDecorator;
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Class EntityManagerWrapper
|
||||
*/
|
||||
class EntityManagerWrapper extends DefaultPluginManager implements EntityTypeManagerInterface, ContainerAwareInterface {
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $loaded;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $rendered;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
private $entityManager;
|
||||
|
||||
/**
|
||||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityManager
|
||||
*/
|
||||
public function __construct(EntityTypeManagerInterface $entityManager) {
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getStorage($entity_type) {
|
||||
/** @var ConfigEntityStorageInterface $handler */
|
||||
$handler = $this->getHandler($entity_type, 'storage');
|
||||
$type = ($handler instanceof ConfigEntityStorageInterface) ? 'config' : 'content';
|
||||
|
||||
if (!isset($this->loaded[$type][$entity_type])) {
|
||||
$handler = $this->getStorageDecorator($entity_type, $handler);
|
||||
$this->loaded[$type][$entity_type] = $handler;
|
||||
}
|
||||
else {
|
||||
$handler = $this->loaded[$type][$entity_type];
|
||||
}
|
||||
|
||||
return $handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getViewBuilder($entity_type) {
|
||||
/** @var EntityViewBuilderInterface $handler */
|
||||
$handler = $this->getHandler($entity_type, 'view_builder');
|
||||
|
||||
if ($handler instanceof EntityViewBuilderInterface) {
|
||||
if (!isset($this->rendered[$entity_type])) {
|
||||
$handler = new EntityViewBuilderDecorator($handler);
|
||||
$this->rendered[$entity_type] = $handler;
|
||||
}
|
||||
else {
|
||||
$handler = $this->rendered[$entity_type];
|
||||
}
|
||||
}
|
||||
|
||||
return $handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $entity_type
|
||||
* @param $handler
|
||||
*
|
||||
* @return \Drupal\webprofiler\Entity\EntityDecorator
|
||||
*/
|
||||
private function getStorageDecorator($entity_type, $handler) {
|
||||
if ($handler instanceof ConfigEntityStorageInterface) {
|
||||
switch ($entity_type) {
|
||||
case 'taxonomy_vocabulary':
|
||||
return new VocabularyStorageDecorator($handler);
|
||||
break;
|
||||
case 'user_role':
|
||||
return new RoleStorageDecorator($handler);
|
||||
break;
|
||||
case 'shortcut_set':
|
||||
return new ShortcutSetStorageDecorator($handler);
|
||||
break;
|
||||
default:
|
||||
return new ConfigEntityStorageDecorator($handler);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $type
|
||||
* @param $entity_type
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getLoaded($type, $entity_type) {
|
||||
return isset($this->loaded[$type][$entity_type]) ? $this->loaded[$type][$entity_type] : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $entity_type
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getRendered( $entity_type) {
|
||||
return isset($this->rendered[$entity_type]) ? $this->rendered[$entity_type] : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function useCaches($use_caches = FALSE) {
|
||||
$this->entityManager->useCaches($use_caches);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasDefinition($plugin_id) {
|
||||
return $this->entityManager->hasDefinition($plugin_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAccessControlHandler($entity_type) {
|
||||
return $this->entityManager->getAccessControlHandler($entity_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function clearCachedDefinitions() {
|
||||
$this->entityManager->clearCachedDefinitions();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getListBuilder($entity_type) {
|
||||
return $this->entityManager->getListBuilder($entity_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormObject($entity_type, $operation) {
|
||||
return $this->entityManager->getFormObject($entity_type, $operation);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRouteProviders($entity_type) {
|
||||
return $this->entityManager->getRouteProviders($entity_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasHandler($entity_type, $handler_type) {
|
||||
return $this->entityManager->hasHandler($entity_type, $handler_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getHandler($entity_type, $handler_type) {
|
||||
return $this->entityManager->getHandler($entity_type, $handler_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createHandlerInstance(
|
||||
$class,
|
||||
EntityTypeInterface $definition = NULL
|
||||
) {
|
||||
return $this->entityManager->createHandlerInstance($class, $definition);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinition($entity_type_id, $exception_on_invalid = TRUE) {
|
||||
return $this->entityManager->getDefinition(
|
||||
$entity_type_id,
|
||||
$exception_on_invalid
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinitions() {
|
||||
return $this->entityManager->getDefinitions();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createInstance($plugin_id, array $configuration = []) {
|
||||
return $this->entityManager->createInstance($plugin_id, $configuration);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInstance(array $options) {
|
||||
return $this->entityManager->getInstance($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setContainer(ContainerInterface $container = NULL) {
|
||||
$this->entityManager->setContainer($container);
|
||||
}
|
||||
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Entity;
|
||||
|
||||
use Drupal\Core\Entity\EntityHandlerInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Entity\EntityViewBuilderInterface;
|
||||
use Drupal\Core\Field\FieldItemInterface;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Class EntityViewBuilderDecorator
|
||||
*/
|
||||
class EntityViewBuilderDecorator extends EntityDecorator implements EntityHandlerInterface, EntityViewBuilderInterface {
|
||||
|
||||
/**
|
||||
* @param EntityViewBuilderInterface $controller
|
||||
*/
|
||||
public function __construct(EntityViewBuilderInterface $controller) {
|
||||
parent::__construct($controller);
|
||||
|
||||
$this->entities = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildComponents(array &$build, array $entities, array $displays, $view_mode, $langcode = NULL) {
|
||||
$this->getOriginalObject()
|
||||
->buildComponents($build, $entities, $displays, $view_mode, $langcode);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function view(EntityInterface $entity, $view_mode = 'full', $langcode = NULL) {
|
||||
$this->entities[] = $entity;
|
||||
|
||||
return $this->getOriginalObject()->view($entity, $view_mode, $langcode);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function viewMultiple(array $entities = [], $view_mode = 'full', $langcode = NULL) {
|
||||
$this->entities = array_merge($this->entities, $entities);
|
||||
|
||||
return $this->getOriginalObject()
|
||||
->viewMultiple($entities, $view_mode, $langcode);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function resetCache(array $entities = NULL) {
|
||||
$this->getOriginalObject()->resetCache($entities);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function viewField(FieldItemListInterface $items, $display_options = []) {
|
||||
return $this->getOriginalObject()->viewField($items, $display_options);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function viewFieldItem(FieldItemInterface $item, $display_options = []) {
|
||||
return $this->getOriginalObject()->viewFieldItem($item, $display_options);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheTags() {
|
||||
return $this->getOriginalObject()->getCacheTag();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
|
||||
return new static(
|
||||
$entity_type,
|
||||
$container->get('entity.manager'),
|
||||
$container->get('language_manager')
|
||||
);
|
||||
}
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\EventSubscriber;
|
||||
|
||||
use Drupal\Core\Render\RendererInterface;
|
||||
use Drupal\Core\Routing\UrlGeneratorInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
|
||||
/**
|
||||
* Class WebprofilerEventSubscriber
|
||||
*/
|
||||
class WebprofilerEventSubscriber implements EventSubscriberInterface {
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Session\AccountInterface
|
||||
*/
|
||||
private $currentUser;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Routing\UrlGeneratorInterface
|
||||
*/
|
||||
protected $urlGenerator;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Render\RendererInterface
|
||||
*/
|
||||
protected $renderer;
|
||||
|
||||
/**
|
||||
* @param \Drupal\Core\Session\AccountInterface $currentUser
|
||||
* @param \Drupal\Core\Routing\UrlGeneratorInterface $urlGenerator
|
||||
* @param \Drupal\Core\Render\RendererInterface $renderer
|
||||
*/
|
||||
public function __construct(AccountInterface $currentUser, UrlGeneratorInterface $urlGenerator, RendererInterface $renderer) {
|
||||
$this->currentUser = $currentUser;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->renderer = $renderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Symfony\Component\HttpKernel\Event\FilterResponseEvent $event
|
||||
*/
|
||||
public function onKernelResponse(FilterResponseEvent $event) {
|
||||
$response = $event->getResponse();
|
||||
$request = $event->getRequest();
|
||||
|
||||
if ($response->headers->has('X-Debug-Token') && NULL !== $this->urlGenerator) {
|
||||
$response->headers->set(
|
||||
'X-Debug-Token-Link',
|
||||
$this->urlGenerator->generate('webprofiler.dashboard', ['profile' => $response->headers->get('X-Debug-Token')])
|
||||
);
|
||||
}
|
||||
|
||||
// do not capture redirects or modify XML HTTP Requests
|
||||
if ($request->isXmlHttpRequest()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->currentUser->hasPermission('view webprofiler toolbar')) {
|
||||
$this->injectToolbar($response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Symfony\Component\HttpFoundation\Response $response
|
||||
*/
|
||||
protected function injectToolbar(Response $response) {
|
||||
$content = $response->getContent();
|
||||
$pos = mb_strripos($content, '</body>');
|
||||
|
||||
if (FALSE !== $pos) {
|
||||
if ($token = $response->headers->get('X-Debug-Token')) {
|
||||
$loader = [
|
||||
'#theme' => 'webprofiler_loader',
|
||||
'#token' => $token,
|
||||
'#profiler_url' => $this->urlGenerator->generate('webprofiler.toolbar', ['profile' => $token]),
|
||||
];
|
||||
|
||||
$content = mb_substr($content, 0, $pos) . $this->renderer->renderRoot($loader) . mb_substr($content, $pos);
|
||||
$response->setContent($content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function getSubscribedEvents() {
|
||||
return [
|
||||
KernelEvents::RESPONSE => ['onKernelResponse', -128],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Form;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Form\ConfigFormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\webprofiler\Profiler\ProfilerStorageManager;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpKernel\Profiler\Profiler;
|
||||
|
||||
/**
|
||||
* Class ConfigForm
|
||||
*/
|
||||
class ConfigForm extends ConfigFormBase {
|
||||
|
||||
/**
|
||||
* @var \Symfony\Component\HttpKernel\Profiler\Profiler
|
||||
*/
|
||||
private $profiler;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $templates;
|
||||
|
||||
/**
|
||||
* @var \Drupal\webprofiler\Profiler\ProfilerStorageManager
|
||||
*/
|
||||
private $storageManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('config.factory'),
|
||||
$container->get('profiler'),
|
||||
$container->get('profiler.storage_manager'),
|
||||
$container->getParameter('data_collector.templates')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* @param Profiler $profiler
|
||||
* @param \Drupal\webprofiler\Profiler\ProfilerStorageManager $storageManager
|
||||
* @param array $templates
|
||||
*/
|
||||
public function __construct(ConfigFactoryInterface $config_factory, Profiler $profiler, ProfilerStorageManager $storageManager, $templates) {
|
||||
parent::__construct($config_factory);
|
||||
|
||||
$this->profiler = $profiler;
|
||||
$this->templates = $templates;
|
||||
$this->storageManager = $storageManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'webprofiler_config';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$config = $this->config('webprofiler.config');
|
||||
|
||||
$form['purge_on_cache_clear'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Purge on cache clear'),
|
||||
'#description' => $this->t('Deletes all profiler files during cache clear.'),
|
||||
'#default_value' => $config->get('purge_on_cache_clear'),
|
||||
];
|
||||
|
||||
$storages = $this->storageManager->getStorages();
|
||||
|
||||
$form['storage'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Storage backend'),
|
||||
'#description' => $this->t('Choose were to store profiler data.'),
|
||||
'#options' => $storages,
|
||||
'#default_value' => $config->get('storage'),
|
||||
];
|
||||
|
||||
$form['exclude'] = [
|
||||
'#type' => 'textarea',
|
||||
'#title' => $this->t('Exclude'),
|
||||
'#default_value' => $config->get('exclude'),
|
||||
'#description' => $this->t('Paths to exclude for profiling. One path per line.'),
|
||||
];
|
||||
|
||||
$form['active_toolbar_items'] = [
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => $this->t('Active toolbar items'),
|
||||
'#options' => $this->getCollectors(),
|
||||
'#description' => $this->t('Choose which items to show into the toolbar.'),
|
||||
'#default_value' => $config->get('active_toolbar_items'),
|
||||
];
|
||||
|
||||
$form['ide_settings'] = [
|
||||
'#type' => 'details',
|
||||
'#title' => $this->t('IDE settings'),
|
||||
'#open' => FALSE,
|
||||
];
|
||||
|
||||
$form['ide_settings']['ide_link'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('IDE link'),
|
||||
'#description' => $this->t('IDE link for open files.'),
|
||||
'#default_value' => $config->get('ide_link'),
|
||||
];
|
||||
|
||||
$form['ide_settings']['ide_link_remote'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('IDE link remote path'),
|
||||
'#description' => $this->t('The path of the remote docroot. Leave blank if the docroot is on the same machine of the IDE. No trailing slash.'),
|
||||
'#default_value' => $config->get('ide_link_remote'),
|
||||
];
|
||||
|
||||
$form['ide_settings']['ide_link_local'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('IDE link local path'),
|
||||
'#description' => $this->t('The path of the local docroot. Leave blank if the docroot is on the same machine of IDE. No trailing slash.'),
|
||||
'#default_value' => $config->get('ide_link_local'),
|
||||
];
|
||||
|
||||
$form['database'] = [
|
||||
'#type' => 'details',
|
||||
'#title' => $this->t('Database settings'),
|
||||
'#open' => FALSE,
|
||||
'#states' => array(
|
||||
'visible' => array(
|
||||
array(
|
||||
':input[name="active_toolbar_items[database]' => array('checked' => TRUE),
|
||||
),
|
||||
),
|
||||
),
|
||||
];
|
||||
|
||||
$form['database']['query_sort'] = [
|
||||
'#type' => 'radios',
|
||||
'#title' => $this->t('Sort query log'),
|
||||
'#options' => ['source' => 'by source', 'duration' => 'by duration'],
|
||||
'#description' => $this->t('The query table can be sorted in the order that the queries were executed or by descending duration.'),
|
||||
'#default_value' => $config->get('query_sort'),
|
||||
];
|
||||
|
||||
$form['database']['query_highlight'] = [
|
||||
'#type' => 'number',
|
||||
'#title' => $this->t('Slow query highlighting'),
|
||||
'#description' => $this->t('Enter an integer in milliseconds. Any query which takes longer than this many milliseconds will be highlighted in the query log. This indicates a possibly inefficient query, or a candidate for caching.'),
|
||||
'#default_value' => $config->get('query_highlight'),
|
||||
'#min' => 0,
|
||||
];
|
||||
|
||||
$storageId = $this->config('webprofiler.config')->get('storage');
|
||||
$storage = $this->storageManager->getStorage($storageId);
|
||||
|
||||
$form['purge'] = [
|
||||
'#type' => 'details',
|
||||
'#title' => $this->t('Purge profiles'),
|
||||
'#open' => FALSE,
|
||||
];
|
||||
|
||||
$form['purge']['purge'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Purge'),
|
||||
'#submit' => [[$this, 'purge']],
|
||||
];
|
||||
|
||||
$form['purge']['purge-help'] = [
|
||||
'#type' => 'inline_template',
|
||||
'#template' => '<div class="form-item">{{ message }}</div>',
|
||||
'#context' => [
|
||||
'message' => $this->t('Purge %storage profiles.', ['%storage' => $storage['title']]),
|
||||
],
|
||||
];
|
||||
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->config('webprofiler.config')
|
||||
->set('purge_on_cache_clear', $form_state->getValue('purge_on_cache_clear'))
|
||||
->set('storage', $form_state->getValue('storage'))
|
||||
->set('exclude', $form_state->getValue('exclude'))
|
||||
->set('active_toolbar_items', $form_state->getValue('active_toolbar_items'))
|
||||
->set('ide_link', $form_state->getValue('ide_link'))
|
||||
->set('ide_link_remote', $form_state->getValue('ide_link_remote'))
|
||||
->set('ide_link_local', $form_state->getValue('ide_link_local'))
|
||||
->set('query_sort', $form_state->getValue('query_sort'))
|
||||
->set('query_highlight', $form_state->getValue('query_highlight'))
|
||||
->save();
|
||||
|
||||
parent::submitForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Purges profiles.
|
||||
*/
|
||||
public function purge(array &$form, FormStateInterface $form_state) {
|
||||
$this->profiler->purge();
|
||||
drupal_set_message($this->t('Profiles purged'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function getCollectors() {
|
||||
$options = [];
|
||||
foreach ($this->templates as $template) {
|
||||
// drupal collector should not be disabled
|
||||
if ($template[0] != 'drupal') {
|
||||
$options[$template[0]] = $template[2];
|
||||
}
|
||||
}
|
||||
|
||||
asort($options);
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getEditableConfigNames() {
|
||||
return [
|
||||
'webprofiler.config',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Form;
|
||||
|
||||
use Drupal\Core\Form\FormBuilder;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Class FormBuilderWrapper
|
||||
*/
|
||||
class FormBuilderWrapper extends FormBuilder {
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $buildForms;
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getBuildForm() {
|
||||
return $this->buildForms;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prepareForm($form_id, &$form, FormStateInterface &$form_state) {
|
||||
parent::prepareForm($form_id, $form, $form_state);
|
||||
|
||||
if (!$this->buildForms) {
|
||||
$this->buildForms = [];
|
||||
}
|
||||
|
||||
$elements = [];
|
||||
foreach ($form as $key => $value) {
|
||||
if (strpos($key, '#') !== 0) {
|
||||
$elements[$key]['#title'] = isset($value['#title']) ? $value['#title'] : NULL;
|
||||
$elements[$key]['#access'] = isset($value['#access']) ? $value['#access'] : NULL;
|
||||
$elements[$key]['#type'] = isset($value['#type']) ? $value['#type'] : NULL;
|
||||
}
|
||||
}
|
||||
|
||||
$buildInfo = $form_state->getBuildInfo();
|
||||
|
||||
$class = get_class($buildInfo['callback_object']);
|
||||
$method = new \ReflectionMethod($class, 'buildForm');
|
||||
|
||||
$this->buildForms[$buildInfo['form_id']] = [
|
||||
'class' => [
|
||||
'class' => $class,
|
||||
'method' => 'buildForm',
|
||||
'file' => $method->getFilename(),
|
||||
'line' => $method->getStartLine(),
|
||||
],
|
||||
'form' => $elements,
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Form;
|
||||
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Class ProfilesFilterForm
|
||||
*/
|
||||
class ProfilesFilterForm extends FormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'webprofiler_profiles_filter';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$form['ip'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('IP'),
|
||||
'#size' => 30,
|
||||
'#default_value' => $this->getRequest()->query->get('ip'),
|
||||
'#prefix' => '<div class="form--inline clearfix">',
|
||||
];
|
||||
|
||||
$form['url'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Url'),
|
||||
'#size' => 30,
|
||||
'#default_value' => $this->getRequest()->query->get('url'),
|
||||
];
|
||||
|
||||
$form['method'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Method'),
|
||||
'#options' => ['GET' => 'GET', 'POST' => 'POST'],
|
||||
'#default_value' => $this->getRequest()->query->get('method'),
|
||||
];
|
||||
|
||||
$limits = [10, 50, 100];
|
||||
$form['limit'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Limit'),
|
||||
'#options' => array_combine($limits, $limits),
|
||||
'#default_value' => $this->getRequest()->query->get('limit'),
|
||||
];
|
||||
|
||||
$form['actions'] = ['#type' => 'actions'];
|
||||
$form['actions']['filter'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Filter'),
|
||||
'#attributes' => ['class' => ['button--primary']],
|
||||
'#suffix' => '</div>',
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$ip = $form_state->getValue('ip');// ['values']['ip'];
|
||||
$url = $form_state->getValue('url');
|
||||
$method = $form_state->getValue('method');
|
||||
$limit = $form_state->getValue('limit');
|
||||
|
||||
$url = new Url('webprofiler.admin_list', [], [
|
||||
'query' => [
|
||||
'ip' => $ip,
|
||||
'url' => $url,
|
||||
'method' => $method,
|
||||
'limit' => $limit,
|
||||
]
|
||||
]);
|
||||
|
||||
$form_state->setRedirectUrl($url);
|
||||
}
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Frontend;
|
||||
|
||||
/**
|
||||
* Class PerformanceTimingData
|
||||
*/
|
||||
class PerformanceTimingData {
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $data;
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*/
|
||||
public function __construct($data) {
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getDNSTiming() {
|
||||
if (isset($this->data['domainLookupEnd']) && isset($this->data['domainLookupStart'])) {
|
||||
return $this->data['domainLookupEnd'] - $this->data['domainLookupStart'];
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getTCPTiming() {
|
||||
if (isset($this->data['connectEnd']) && isset($this->data['connectStart'])) {
|
||||
return $this->data['connectEnd'] - $this->data['connectStart'];
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getTtfbTiming() {
|
||||
if (isset($this->data['responseStart']) && isset($this->data['connectEnd'])) {
|
||||
return $this->data['responseStart'] - $this->data['connectEnd'];
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getDataTiming() {
|
||||
if (isset($this->data['responseEnd']) && isset($this->data['responseStart'])) {
|
||||
return $this->data['responseEnd'] - $this->data['responseStart'];
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getDomTiming() {
|
||||
if (isset($this->data['loadEventStart']) && isset($this->data['responseEnd'])) {
|
||||
return $this->data['loadEventStart'] - $this->data['responseEnd'];
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Helper;
|
||||
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
|
||||
/**
|
||||
* Class ClassShortener
|
||||
*/
|
||||
class ClassShortener implements ClassShortenerInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function shortenClass($class) {
|
||||
$parts = explode('\\', $class);
|
||||
$result = [];
|
||||
$size = count($parts) - 1;
|
||||
|
||||
foreach ($parts as $key => $part) {
|
||||
if ($key < $size) {
|
||||
$result[] = substr($part, 0, 1);
|
||||
}
|
||||
else {
|
||||
$result[] = $part;
|
||||
}
|
||||
}
|
||||
|
||||
return new FormattableMarkup("<abbr title=\"@class\">@short</abbr>", [
|
||||
'@class' => $class,
|
||||
'@short' => implode('\\', $result)
|
||||
]);
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Helper;
|
||||
|
||||
/**
|
||||
* Interface ClassShortenerInterface
|
||||
*/
|
||||
interface ClassShortenerInterface {
|
||||
|
||||
/**
|
||||
* @param string $class
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function shortenClass($class);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Helper;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
|
||||
/**
|
||||
* Class IdeLinkGenerator.
|
||||
*/
|
||||
class IdeLinkGenerator implements IdeLinkGeneratorInterface {
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||
*/
|
||||
private $config_factory;
|
||||
|
||||
/**
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
*/
|
||||
public function __construct(ConfigFactoryInterface $config_factory) {
|
||||
$this->config_factory = $config_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function generateLink($file, $line) {
|
||||
$ide_link = $this->config_factory->get('webprofiler.config')
|
||||
->get('ide_link');
|
||||
$ide_link_remote = $this->config_factory->get('webprofiler.config')
|
||||
->get('ide_link_remote');
|
||||
$ide_link_local = $this->config_factory->get('webprofiler.config')
|
||||
->get('ide_link_local');
|
||||
|
||||
$file = str_replace($ide_link_remote, $ide_link_local, $file);
|
||||
|
||||
return new FormattableMarkup($ide_link, ['@file' => $file, '@line' => $line]);
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Helper;
|
||||
|
||||
/**
|
||||
* Interface IdeLinkGeneratorInterface.
|
||||
*/
|
||||
interface IdeLinkGeneratorInterface {
|
||||
|
||||
/**
|
||||
* @param $file
|
||||
* @param $line
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function generateLink($file, $line);
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Http;
|
||||
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use GuzzleHttp\TransferStats;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* Class HttpClientMiddleware
|
||||
*/
|
||||
class HttpClientMiddleware {
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $completedRequests;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $failedRequests;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->completedRequests = [];
|
||||
$this->failedRequests = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __invoke() {
|
||||
return function ($handler) {
|
||||
return function (RequestInterface $request, array $options) use ($handler) {
|
||||
|
||||
$options['on_stats'] = function (TransferStats $stats) use ($request) {
|
||||
$request->stats = $stats;
|
||||
};
|
||||
|
||||
return $handler($request, $options)->then(
|
||||
function ($response) use ($request) {
|
||||
|
||||
$this->completedRequests[] = [
|
||||
'request' => $request,
|
||||
'response' => $response,
|
||||
];
|
||||
|
||||
return $response;
|
||||
},
|
||||
function ($reason) use ($request) {
|
||||
$response = $reason instanceof RequestException
|
||||
? $reason->getResponse()
|
||||
: null;
|
||||
|
||||
$this->failedRequests[] = [
|
||||
'request' => $request,
|
||||
'response' => $response,
|
||||
'message' => $reason->getMessage(),
|
||||
];
|
||||
|
||||
return \GuzzleHttp\Promise\rejection_for($reason);
|
||||
}
|
||||
);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getCompletedRequests() {
|
||||
return $this->completedRequests;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getFailedRequests() {
|
||||
return $this->failedRequests;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Mail;
|
||||
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
|
||||
use Drupal\Core\Mail\MailManagerInterface;
|
||||
use Drupal\Core\Plugin\DefaultPluginManager;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\Core\StringTranslation\TranslationInterface;
|
||||
use Drupal\webprofiler\DataCollector\MailDataCollector;
|
||||
|
||||
/**
|
||||
* Class MailManagerWrapper
|
||||
*/
|
||||
class MailManagerWrapper extends DefaultPluginManager implements MailManagerInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* @var \Drupal\webprofiler\DataCollector\MailDataCollector
|
||||
*/
|
||||
private $dataCollector;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Mail\MailManagerInterface
|
||||
*/
|
||||
private $mailManager;
|
||||
|
||||
/**
|
||||
* The config factory.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||
*/
|
||||
protected $configFactory;
|
||||
|
||||
/**
|
||||
* The logger factory.
|
||||
*
|
||||
* @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
|
||||
*/
|
||||
protected $loggerFactory;
|
||||
|
||||
/**
|
||||
* List of already instantiated mail plugins.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $instances = array();
|
||||
|
||||
/**
|
||||
* Constructs the MailManager object.
|
||||
*
|
||||
* @param \Traversable $namespaces
|
||||
* An object that implements \Traversable which contains the root paths
|
||||
* keyed by the corresponding namespace to look for plugin implementations.
|
||||
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
|
||||
* Cache backend instance to use.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler to invoke the alter hook with.
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The configuration factory.
|
||||
* @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
|
||||
* The logger channel factory.
|
||||
* @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
|
||||
* The string translation service.
|
||||
* @param \Drupal\Core\Mail\MailManagerInterface $mailManager
|
||||
* @param \Drupal\webprofiler\DataCollector\MailDataCollector $dataCollector
|
||||
*/
|
||||
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, ConfigFactoryInterface $config_factory, LoggerChannelFactoryInterface $logger_factory, TranslationInterface $string_translation, MailManagerInterface $mailManager, MailDataCollector $dataCollector) {
|
||||
parent::__construct('Plugin/Mail', $namespaces, $module_handler, 'Drupal\Core\Mail\MailInterface', 'Drupal\Core\Annotation\Mail');
|
||||
$this->alterInfo('mail_backend_info');
|
||||
$this->setCacheBackend($cache_backend, 'mail_backend_plugins');
|
||||
$this->configFactory = $config_factory;
|
||||
$this->loggerFactory = $logger_factory;
|
||||
$this->stringTranslation = $string_translation;
|
||||
$this->dataCollector = $dataCollector;
|
||||
$this->mailManager = $mailManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function mail($module, $key, $to, $langcode, $params = array(), $reply = NULL, $send = TRUE) {
|
||||
$message = $this->mailManager->mail($module, $key, $to, $langcode, $params, $reply, $send);
|
||||
|
||||
$instance = $this->mailManager->getInstance(['module' => $module, 'key' => $key]);
|
||||
$this->dataCollector->addMessage($message, $instance);
|
||||
|
||||
return $message;
|
||||
}
|
||||
}
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Profiler;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Symfony\Component\HttpKernel\Profiler\Profile;
|
||||
use Symfony\Component\HttpKernel\Profiler\ProfilerStorageInterface;
|
||||
|
||||
/**
|
||||
* Implements a profiler storage using the DBTNG query api.
|
||||
*/
|
||||
class DatabaseProfilerStorage implements ProfilerStorageInterface {
|
||||
|
||||
/**
|
||||
* The database connection.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $database;
|
||||
|
||||
/**
|
||||
* Constructs a new DatabaseProfilerStorage instance.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Connection $database
|
||||
* The database connection.
|
||||
*/
|
||||
function __construct(Connection $database) {
|
||||
$this->database = $database;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function find($ip, $url, $limit, $method, $start = NULL, $end = NULL) {
|
||||
$select = $this->database->select('webprofiler', 'wp', ['fetch' => \PDO::FETCH_ASSOC]);
|
||||
|
||||
if (NULL === $start) {
|
||||
$start = 0;
|
||||
}
|
||||
|
||||
if (NULL === $end) {
|
||||
$end = time();
|
||||
}
|
||||
|
||||
if ($ip = preg_replace('/[^\d\.]/', '', $ip)) {
|
||||
$select->condition('ip', '%' . $this->database->escapeLike($ip) . '%', 'LIKE');
|
||||
}
|
||||
|
||||
if ($url) {
|
||||
$select->condition('url', '%' . $this->database->escapeLike(addcslashes($url, '%_\\')) . '%', 'LIKE');
|
||||
}
|
||||
|
||||
if ($method) {
|
||||
$select->condition('method', $method);
|
||||
}
|
||||
|
||||
if (!empty($start)) {
|
||||
$select->condition('time', $start, '>=');
|
||||
}
|
||||
|
||||
if (!empty($end)) {
|
||||
$select->condition('time', $end, '<=');
|
||||
}
|
||||
|
||||
$select->fields('wp', [
|
||||
'token',
|
||||
'ip',
|
||||
'method',
|
||||
'url',
|
||||
'time',
|
||||
'parent',
|
||||
'status_code'
|
||||
]);
|
||||
$select->orderBy('time', 'DESC');
|
||||
$select->range(0, $limit);
|
||||
return $select->execute()
|
||||
->fetchAllAssoc('token');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function read($token) {
|
||||
$record = $this->database->select('webprofiler', 'w')
|
||||
->fields('w')
|
||||
->condition('token', $token)
|
||||
->execute()
|
||||
->fetch();
|
||||
if (isset($record->data)) {
|
||||
return $this->createProfileFromData($token, $record);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function write(Profile $profile) {
|
||||
$args = [
|
||||
'token' => $profile->getToken(),
|
||||
'parent' => $profile->getParentToken(),
|
||||
'data' => base64_encode(serialize($profile->getCollectors())),
|
||||
'ip' => $profile->getIp(),
|
||||
'method' => $profile->getMethod(),
|
||||
'url' => $profile->getUrl(),
|
||||
'time' => $profile->getTime(),
|
||||
'created_at' => time(),
|
||||
'status_code' => $profile->getStatusCode(),
|
||||
];
|
||||
|
||||
try {
|
||||
$query = $this->database->select('webprofiler', 'w')
|
||||
->fields('w', ['token']);
|
||||
$query->condition('token', $profile->getToken());
|
||||
$count = $query->countQuery()->execute()->fetchAssoc();
|
||||
|
||||
if ($count['expression']) {
|
||||
$this->database->update('webprofiler')
|
||||
->fields($args)
|
||||
->condition('token', $profile->getToken())
|
||||
->execute();
|
||||
}
|
||||
else {
|
||||
$this->database->insert('webprofiler')->fields($args)->execute();
|
||||
}
|
||||
|
||||
$status = TRUE;
|
||||
} catch (\Exception $e) {
|
||||
$status = FALSE;
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function purge() {
|
||||
$this->database->truncate('webprofiler')->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $token
|
||||
* @param $data
|
||||
*
|
||||
* @return Profile
|
||||
*/
|
||||
private function createProfileFromData($token, $data) {
|
||||
$profile = new Profile($token);
|
||||
$profile->setIp($data->ip);
|
||||
$profile->setMethod($data->method);
|
||||
$profile->setUrl($data->url);
|
||||
$profile->setTime($data->time);
|
||||
$profile->setCollectors(unserialize(base64_decode($data->data)));
|
||||
$profile->setStatusCode($data->status_code);
|
||||
|
||||
return $profile;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Profiler;
|
||||
|
||||
use Symfony\Component\HttpKernel\Profiler\FileProfilerStorage as SymfonyFileProfilerStorage;
|
||||
use Symfony\Component\HttpKernel\Profiler\Profile;
|
||||
|
||||
class FileProfilerStorage extends SymfonyFileProfilerStorage {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createProfileFromData($token, $data, $parent = NULL) {
|
||||
$profile = new Profile($token);
|
||||
$profile->setIp($data['ip']);
|
||||
$profile->setMethod($data['method']);
|
||||
$profile->setUrl($data['url']);
|
||||
$profile->setTime($data['time']);
|
||||
$profile->setCollectors($data['data']);
|
||||
$profile->setStatusCode($data['status_code']);
|
||||
|
||||
return $profile;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Profiler;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface;
|
||||
use Symfony\Component\HttpKernel\Profiler\Profile;
|
||||
use Symfony\Component\HttpKernel\Profiler\Profiler as SymfonyProfiler;
|
||||
use Symfony\Component\HttpKernel\Profiler\ProfilerStorageInterface;
|
||||
|
||||
/**
|
||||
* Class Profiler
|
||||
*/
|
||||
class Profiler extends SymfonyProfiler {
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface $config
|
||||
*/
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $activeToolbarItems;
|
||||
|
||||
private $localStorage;
|
||||
private $localLogger;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \Symfony\Component\HttpKernel\Profiler\ProfilerStorageInterface $storage
|
||||
* A ProfilerStorageInterface instance
|
||||
* @param \Psr\Log\LoggerInterface $logger
|
||||
* A LoggerInterface instance
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config
|
||||
*/
|
||||
public function __construct(ProfilerStorageInterface $storage, LoggerInterface $logger = NULL, ConfigFactoryInterface $config) {
|
||||
parent::__construct($storage, $logger);
|
||||
|
||||
$this->localStorage = $storage;
|
||||
$this->localLogger = $logger;
|
||||
|
||||
$this->config = $config;
|
||||
$this->activeToolbarItems = $this->config->get('webprofiler.config')
|
||||
->get('active_toolbar_items');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function add(DataCollectorInterface $collector) {
|
||||
// drupal collector should not be disabled
|
||||
if ($collector->getName() == 'drupal') {
|
||||
parent::add($collector);
|
||||
}
|
||||
else {
|
||||
if ($this->activeToolbarItems && array_key_exists($collector->getName(), $this->activeToolbarItems) && $this->activeToolbarItems[$collector->getName()] !== '0') {
|
||||
parent::add($collector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Symfony\Component\HttpKernel\Profiler\Profile $profile
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function updateProfile(Profile $profile) {
|
||||
if (!($ret = $this->localStorage->write($profile)) && NULL !== $this->localLogger) {
|
||||
$this->localLogger->warning('Unable to store the profiler information.');
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Profiler;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Class ProfilerStorageFactory
|
||||
*/
|
||||
class ProfilerStorageFactory {
|
||||
|
||||
/**
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config
|
||||
* @param \Symfony\Component\DependencyInjection\ContainerInterface $container
|
||||
*
|
||||
* @return \Symfony\Component\HttpKernel\Profiler\ProfilerStorageInterface
|
||||
*/
|
||||
final public static function getProfilerStorage(ConfigFactoryInterface $config, ContainerInterface $container) {
|
||||
$storage = $config->get('webprofiler.config')
|
||||
->get('storage') ?: 'profiler.database_storage';
|
||||
|
||||
return $container->get($storage);
|
||||
}
|
||||
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Profiler;
|
||||
|
||||
use Symfony\Component\HttpKernel\Profiler\ProfilerStorageInterface;
|
||||
|
||||
/**
|
||||
* Class ProfilerStorageManager
|
||||
*/
|
||||
class ProfilerStorageManager {
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $storages;
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getStorages() {
|
||||
$output = [];
|
||||
|
||||
/** @var \Symfony\Component\HttpKernel\Profiler\ProfilerStorageInterface $storage */
|
||||
foreach ($this->storages as $id => $storage) {
|
||||
$output[$id] = $storage['title'];
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $id
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getStorage($id) {
|
||||
return $this->storages[$id];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $id
|
||||
* @param $title
|
||||
* @param \Symfony\Component\HttpKernel\Profiler\ProfilerStorageInterface $storage
|
||||
*/
|
||||
public function addStorage($id, $title, ProfilerStorageInterface $storage) {
|
||||
$this->storages[$id] = [
|
||||
'title' => $title,
|
||||
'class' => $storage,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Drupal\webprofiler\Profiler;
|
||||
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Symfony\Component\HttpKernel\Profiler\Profiler as SymfonyProfiler;
|
||||
use Symfony\Component\HttpKernel\Profiler\Profile;
|
||||
|
||||
/**
|
||||
* Profiler Templates Manager
|
||||
*/
|
||||
class TemplateManager {
|
||||
|
||||
/**
|
||||
* @var \Twig_Environment
|
||||
*/
|
||||
protected $twig;
|
||||
|
||||
/**
|
||||
* @var \Twig_Loader_Chain
|
||||
*/
|
||||
protected $twigLoader;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $templates;
|
||||
|
||||
/**
|
||||
* @var \Symfony\Component\HttpKernel\Profiler\Profiler
|
||||
*/
|
||||
protected $profiler;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param SymfonyProfiler $profiler
|
||||
* @param \Twig_Environment $twig
|
||||
* @param \Twig_Loader_Chain $twigLoader
|
||||
* @param array $templates
|
||||
*/
|
||||
public function __construct(SymfonyProfiler $profiler, \Twig_Environment $twig, \Twig_Loader_Chain $twigLoader, array $templates) {
|
||||
$this->profiler = $profiler;
|
||||
$this->twig = $twig;
|
||||
$this->twigLoader = $twigLoader;
|
||||
$this->templates = $templates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the template name for a given panel.
|
||||
*
|
||||
* @param Profile $profile
|
||||
* @param string $panel
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
|
||||
*/
|
||||
public function getName(Profile $profile, $panel) {
|
||||
$templates = $this->getNames($profile);
|
||||
|
||||
if (!isset($templates[$panel])) {
|
||||
throw new NotFoundHttpException(sprintf('Panel "%s" is not registered in profiler or is not present in viewed profile.', $panel));
|
||||
}
|
||||
|
||||
return $templates[$panel];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the templates for a given profile.
|
||||
*
|
||||
* @param \Symfony\Component\HttpKernel\Profiler\Profile $profile
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getTemplates(Profile $profile) {
|
||||
$templates = $this->getNames($profile);
|
||||
foreach ($templates as $name => $template) {
|
||||
$templates[$name] = $this->twig->loadTemplate($template);
|
||||
}
|
||||
|
||||
return $templates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets template names of templates that are present in the viewed profile.
|
||||
*
|
||||
* @param \Symfony\Component\HttpKernel\Profiler\Profile $profile
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
protected function getNames(Profile $profile) {
|
||||
$templates = [];
|
||||
|
||||
foreach ($this->templates as $arguments) {
|
||||
if (NULL === $arguments) {
|
||||
continue;
|
||||
}
|
||||
|
||||
list($name, $template) = $arguments;
|
||||
|
||||
if (!$this->profiler->has($name) || !$profile->hasCollector($name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ('.html.twig' === substr($template, -10)) {
|
||||
$template = substr($template, 0, -10);
|
||||
}
|
||||
|
||||
if (!$this->twigLoader->exists($template . '.html.twig')) {
|
||||
throw new \UnexpectedValueException(sprintf('The profiler template "%s.html.twig" for data collector "%s" does not exist.', $template, $name));
|
||||
}
|
||||
|
||||
$templates[$name] = $template . '.html.twig';
|
||||
}
|
||||
|
||||
return $templates;
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\RequestMatcher;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Path\PathMatcherInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestMatcherInterface;
|
||||
|
||||
/**
|
||||
* Class WebprofilerRequestMatcher
|
||||
*/
|
||||
class WebprofilerRequestMatcher implements RequestMatcherInterface {
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||
*/
|
||||
private $configFactory;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Path\PathMatcherInterface
|
||||
*/
|
||||
private $pathMatcher;
|
||||
|
||||
/**
|
||||
* @param ConfigFactoryInterface $configFactory
|
||||
* @param \Drupal\Core\Path\PathMatcherInterface $pathMatcher
|
||||
*/
|
||||
public function __construct(ConfigFactoryInterface $configFactory, PathMatcherInterface $pathMatcher) {
|
||||
$this->configFactory = $configFactory;
|
||||
$this->pathMatcher = $pathMatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function matches(Request $request) {
|
||||
$path = $request->getPathInfo();
|
||||
|
||||
$patterns = $this->configFactory->get('webprofiler.config')->get('exclude');
|
||||
|
||||
// never add Webprofiler to phpinfo page.
|
||||
$patterns .= "\r\n/admin/reports/status/php";
|
||||
|
||||
// never add Webprofiler to uninstall confirm page.
|
||||
$patterns .= "\r\n/admin/modules/uninstall/*";
|
||||
|
||||
return !$this->pathMatcher->matchPath($path, $patterns);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Routing;
|
||||
|
||||
use Drupal\Core\ParamConverter\ParamConverterInterface;
|
||||
use Drupal\webprofiler\Profiler\Profiler;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
* Class TokenConverter
|
||||
*/
|
||||
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) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
$profile = $this->profiler->loadProfile($value);
|
||||
|
||||
if (NULL === $profile) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return $profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function applies($definition, $name, Route $route) {
|
||||
if (!empty($definition['type']) && $definition['type'] === 'webprofiler:token') {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\StackMiddleware;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
|
||||
/**
|
||||
* Class WebprofilerMiddleware
|
||||
*/
|
||||
class WebprofilerMiddleware implements HttpKernelInterface {
|
||||
|
||||
/**
|
||||
* The decorated kernel.
|
||||
*
|
||||
* @var \Symfony\Component\HttpKernel\HttpKernelInterface
|
||||
*/
|
||||
protected $httpKernel;
|
||||
|
||||
/**
|
||||
* Constructs a WebprofilerMiddleware object.
|
||||
*
|
||||
* @param \Symfony\Component\HttpKernel\HttpKernelInterface $http_kernel
|
||||
* The decorated kernel.
|
||||
*/
|
||||
public function __construct(HttpKernelInterface $http_kernel) {
|
||||
$this->httpKernel = $http_kernel;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {
|
||||
foreach (Database::getAllConnectionInfo() as $key => $info) {
|
||||
Database::startLog('webprofiler', $key);
|
||||
}
|
||||
return $this->httpKernel->handle($request, $type, $catch);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\State;
|
||||
|
||||
use Drupal\Core\State\StateInterface;
|
||||
use Drupal\webprofiler\DataCollector\StateDataCollector;
|
||||
|
||||
/**
|
||||
* Class StateWrapper
|
||||
*/
|
||||
class StateWrapper implements StateInterface {
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\State\StateInterface
|
||||
*/
|
||||
private $state;
|
||||
|
||||
/**
|
||||
* @var \Drupal\webprofiler\DataCollector\StateDataCollector
|
||||
*/
|
||||
private $dataCollector;
|
||||
|
||||
/**
|
||||
* @param \Drupal\Core\State\StateInterface $state
|
||||
* @param \Drupal\webprofiler\DataCollector\StateDataCollector $dataCollector
|
||||
*/
|
||||
public function __construct(StateInterface $state, StateDataCollector $dataCollector) {
|
||||
$this->state = $state;
|
||||
$this->dataCollector = $dataCollector;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get($key, $default = NULL) {
|
||||
$this->dataCollector->addState($key);
|
||||
|
||||
return $this->state->get($key, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMultiple(array $keys) {
|
||||
foreach ($keys as $key) {
|
||||
$this->dataCollector->addState($key);
|
||||
}
|
||||
|
||||
return $this->state->getMultiple($keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function set($key, $value) {
|
||||
$this->state->set($key, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setMultiple(array $data) {
|
||||
$this->state->setMultiple($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete($key) {
|
||||
$this->state->delete($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteMultiple(array $keys) {
|
||||
$this->state->deleteMultiple($keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function resetCache() {
|
||||
$this->state->resetCache();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,543 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler {
|
||||
|
||||
/**
|
||||
* Class Stopwatch
|
||||
*/
|
||||
class Stopwatch extends \Symfony\Component\Stopwatch\Stopwatch {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace Symfony\Component\Stopwatch {
|
||||
|
||||
/**
|
||||
* Class Stopwatch
|
||||
*/
|
||||
class Stopwatch {
|
||||
/**
|
||||
* @var Section[]
|
||||
*/
|
||||
private $sections;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $activeSections;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->sections = $this->activeSections = ['__root__' => new Section('__root__')];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new section or re-opens an existing section.
|
||||
*
|
||||
* @param string|null $id The id of the session to re-open, null to create a new one
|
||||
*
|
||||
* @throws \LogicException When the section to re-open is not reachable
|
||||
*/
|
||||
public function openSection($id = NULL) {
|
||||
$current = end($this->activeSections);
|
||||
|
||||
if (NULL !== $id && NULL === $current->get($id)) {
|
||||
throw new \LogicException(sprintf('The section "%s" has been started at an other level and can not be opened.', $id));
|
||||
}
|
||||
|
||||
$this->start('__section__.child', 'section');
|
||||
$this->activeSections[] = $current->open($id);
|
||||
$this->start('__section__');
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the last started section.
|
||||
*
|
||||
* The id parameter is used to retrieve the events from this section.
|
||||
*
|
||||
* @see getSectionEvents
|
||||
*
|
||||
* @param string $id The identifier of the section
|
||||
*
|
||||
* @throws \LogicException When there's no started section to be stopped
|
||||
*/
|
||||
public function stopSection($id) {
|
||||
$this->stop('__section__');
|
||||
|
||||
if (1 == count($this->activeSections)) {
|
||||
throw new \LogicException('There is no started section to stop.');
|
||||
}
|
||||
|
||||
$this->sections[$id] = array_pop($this->activeSections)->setId($id);
|
||||
$this->stop('__section__.child');
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an event.
|
||||
*
|
||||
* @param string $name The event name
|
||||
* @param string $category The event category
|
||||
*
|
||||
* @return StopwatchEvent A StopwatchEvent instance
|
||||
*/
|
||||
public function start($name, $category = NULL) {
|
||||
return end($this->activeSections)->startEvent($name, $category);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the event was started
|
||||
*
|
||||
* @param string $name The event name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isStarted($name) {
|
||||
return end($this->activeSections)->isEventStarted($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops an event.
|
||||
*
|
||||
* @param string $name The event name
|
||||
*
|
||||
* @return StopwatchEvent A StopwatchEvent instance
|
||||
*/
|
||||
public function stop($name) {
|
||||
return end($this->activeSections)->stopEvent($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops then restarts an event.
|
||||
*
|
||||
* @param string $name The event name
|
||||
*
|
||||
* @return StopwatchEvent A StopwatchEvent instance
|
||||
*/
|
||||
public function lap($name) {
|
||||
return end($this->activeSections)->stopEvent($name)->start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all events for a given section.
|
||||
*
|
||||
* @param string $id A section identifier
|
||||
*
|
||||
* @return StopwatchEvent[] An array of StopwatchEvent instances
|
||||
*/
|
||||
public function getSectionEvents($id) {
|
||||
return isset($this->sections[$id]) ? $this->sections[$id]->getEvents() : [];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @internal This class is for internal usage only
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Section {
|
||||
/**
|
||||
* @var StopwatchEvent[]
|
||||
*/
|
||||
private $events = [];
|
||||
|
||||
/**
|
||||
* @var null|float
|
||||
*/
|
||||
private $origin;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var Section[]
|
||||
*/
|
||||
private $children = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param float|null $origin Set the origin of the events in this section, use null to set their origin to their start time
|
||||
*/
|
||||
public function __construct($origin = NULL) {
|
||||
$this->origin = is_numeric($origin) ? $origin : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the child section.
|
||||
*
|
||||
* @param string $id The child section identifier
|
||||
*
|
||||
* @return Section|null The child section or null when none found
|
||||
*/
|
||||
public function get($id) {
|
||||
foreach ($this->children as $child) {
|
||||
if ($id === $child->getId()) {
|
||||
return $child;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates or re-opens a child section.
|
||||
*
|
||||
* @param string|null $id null to create a new section, the identifier to re-open an existing one.
|
||||
*
|
||||
* @return Section A child section
|
||||
*/
|
||||
public function open($id) {
|
||||
if (NULL === $session = $this->get($id)) {
|
||||
$session = $this->children[] = new self(microtime(TRUE) * 1000);
|
||||
}
|
||||
|
||||
return $session;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string The identifier of the section
|
||||
*/
|
||||
public function getId() {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the session identifier.
|
||||
*
|
||||
* @param string $id The session identifier
|
||||
*
|
||||
* @return Section The current section
|
||||
*/
|
||||
public function setId($id) {
|
||||
$this->id = $id;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an event.
|
||||
*
|
||||
* @param string $name The event name
|
||||
* @param string $category The event category
|
||||
*
|
||||
* @return StopwatchEvent The event
|
||||
*/
|
||||
public function startEvent($name, $category) {
|
||||
if (!isset($this->events[$name])) {
|
||||
$this->events[$name] = new StopwatchEvent($this->origin ?: microtime(TRUE) * 1000, $category);
|
||||
}
|
||||
|
||||
return $this->events[$name]->start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the event was started
|
||||
*
|
||||
* @param string $name The event name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEventStarted($name) {
|
||||
return isset($this->events[$name]) && $this->events[$name]->isStarted();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops an event.
|
||||
*
|
||||
* @param string $name The event name
|
||||
*
|
||||
* @return StopwatchEvent The event
|
||||
*
|
||||
* @throws \LogicException When the event has not been started
|
||||
*/
|
||||
public function stopEvent($name) {
|
||||
if (!isset($this->events[$name])) {
|
||||
throw new \LogicException(sprintf('Event "%s" is not started.', $name));
|
||||
}
|
||||
|
||||
return $this->events[$name]->stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops then restarts an event.
|
||||
*
|
||||
* @param string $name The event name
|
||||
*
|
||||
* @return StopwatchEvent The event
|
||||
*
|
||||
* @throws \LogicException When the event has not been started
|
||||
*/
|
||||
public function lap($name) {
|
||||
return $this->stopEvent($name)->start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the events from this section.
|
||||
*
|
||||
* @return StopwatchEvent[] An array of StopwatchEvent instances
|
||||
*/
|
||||
public function getEvents() {
|
||||
return $this->events;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class StopwatchEvent
|
||||
*/
|
||||
class StopwatchEvent {
|
||||
/**
|
||||
* @var StopwatchPeriod[]
|
||||
*/
|
||||
private $periods = [];
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
private $origin;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $category;
|
||||
|
||||
/**
|
||||
* @var float[]
|
||||
*/
|
||||
private $started = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param float $origin The origin time in milliseconds
|
||||
* @param string|null $category The event category or null to use the default
|
||||
*
|
||||
* @throws \InvalidArgumentException When the raw time is not valid
|
||||
*/
|
||||
public function __construct($origin, $category = NULL) {
|
||||
$this->origin = $this->formatTime($origin);
|
||||
$this->category = is_string($category) ? $category : 'default';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the category.
|
||||
*
|
||||
* @return string The category
|
||||
*/
|
||||
public function getCategory() {
|
||||
return $this->category;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the origin.
|
||||
*
|
||||
* @return float The origin in milliseconds
|
||||
*/
|
||||
public function getOrigin() {
|
||||
return $this->origin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a new event period.
|
||||
*
|
||||
* @return StopwatchEvent The event
|
||||
*/
|
||||
public function start() {
|
||||
$this->started[] = $this->getNow();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the last started event period.
|
||||
*
|
||||
* @throws \LogicException When start wasn't called before stopping
|
||||
*
|
||||
* @return StopwatchEvent The event
|
||||
*
|
||||
* @throws \LogicException When stop() is called without a matching call to start()
|
||||
*/
|
||||
public function stop() {
|
||||
if (!count($this->started)) {
|
||||
throw new \LogicException('stop() called but start() has not been called before.');
|
||||
}
|
||||
|
||||
$this->periods[] = new StopwatchPeriod(array_pop($this->started), $this->getNow());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the event was started
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isStarted() {
|
||||
return !empty($this->started);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the current period and then starts a new one.
|
||||
*
|
||||
* @return StopwatchEvent The event
|
||||
*/
|
||||
public function lap() {
|
||||
return $this->stop()->start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops all non already stopped periods.
|
||||
*/
|
||||
public function ensureStopped() {
|
||||
while (count($this->started)) {
|
||||
$this->stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all event periods.
|
||||
*
|
||||
* @return StopwatchPeriod[] An array of StopwatchPeriod instances
|
||||
*/
|
||||
public function getPeriods() {
|
||||
return $this->periods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the relative time of the start of the first period.
|
||||
*
|
||||
* @return integer The time (in milliseconds)
|
||||
*/
|
||||
public function getStartTime() {
|
||||
return isset($this->periods[0]) ? $this->periods[0]->getStartTime() : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the relative time of the end of the last period.
|
||||
*
|
||||
* @return integer The time (in milliseconds)
|
||||
*/
|
||||
public function getEndTime() {
|
||||
return ($count = count($this->periods)) ? $this->periods[$count - 1]->getEndTime() : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the duration of the events (including all periods).
|
||||
*
|
||||
* @return integer The duration (in milliseconds)
|
||||
*/
|
||||
public function getDuration() {
|
||||
$total = 0;
|
||||
foreach ($this->periods as $period) {
|
||||
$total += $period->getDuration();
|
||||
}
|
||||
|
||||
return $total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the max memory usage of all periods.
|
||||
*
|
||||
* @return integer The memory usage (in bytes)
|
||||
*/
|
||||
public function getMemory() {
|
||||
$memory = 0;
|
||||
foreach ($this->periods as $period) {
|
||||
if ($period->getMemory() > $memory) {
|
||||
$memory = $period->getMemory();
|
||||
}
|
||||
}
|
||||
|
||||
return $memory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current time relative to origin.
|
||||
*
|
||||
* @return float Time in ms
|
||||
*/
|
||||
protected function getNow() {
|
||||
return $this->formatTime(microtime(TRUE) * 1000 - $this->origin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a time.
|
||||
*
|
||||
* @param integer|float $time A raw time
|
||||
*
|
||||
* @return float The formatted time
|
||||
*
|
||||
* @throws \InvalidArgumentException When the raw time is not valid
|
||||
*/
|
||||
private function formatTime($time) {
|
||||
if (!is_numeric($time)) {
|
||||
throw new \InvalidArgumentException('The time must be a numerical value');
|
||||
}
|
||||
|
||||
return round($time, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class StopwatchPeriod
|
||||
*/
|
||||
class StopwatchPeriod {
|
||||
private $start;
|
||||
private $end;
|
||||
private $memory;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param integer $start The relative time of the start of the period (in milliseconds)
|
||||
* @param integer $end The relative time of the end of the period (in milliseconds)
|
||||
*/
|
||||
public function __construct($start, $end) {
|
||||
$this->start = (integer) $start;
|
||||
$this->end = (integer) $end;
|
||||
$this->memory = memory_get_usage(TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the relative time of the start of the period.
|
||||
*
|
||||
* @return integer The time (in milliseconds)
|
||||
*/
|
||||
public function getStartTime() {
|
||||
return $this->start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the relative time of the end of the period.
|
||||
*
|
||||
* @return integer The time (in milliseconds)
|
||||
*/
|
||||
public function getEndTime() {
|
||||
return $this->end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the time spent in this period.
|
||||
*
|
||||
* @return integer The period duration (in milliseconds)
|
||||
*/
|
||||
public function getDuration() {
|
||||
return $this->end - $this->start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the memory usage.
|
||||
*
|
||||
* @return integer The memory usage (in bytes)
|
||||
*/
|
||||
public function getMemory() {
|
||||
return $this->memory;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\StringTranslation;
|
||||
|
||||
use Drupal\Core\StringTranslation\TranslationManager;
|
||||
|
||||
/**
|
||||
* Class TranslationManagerWrapper
|
||||
*/
|
||||
class TranslationManagerWrapper extends TranslationManager {
|
||||
|
||||
/**
|
||||
* @var \Drupal\webprofiler\StringTranslation\TranslationManagerWrapper
|
||||
*/
|
||||
private $translationManager;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $translated;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $untranslated;
|
||||
|
||||
/**
|
||||
* @param \Drupal\webprofiler\StringTranslation\TranslationManagerWrapper $translationManager
|
||||
*/
|
||||
public function setDataCollector(TranslationManagerWrapper $translationManager) {
|
||||
$this->translationManager = $translationManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doTranslate($string, array $options = array()) {
|
||||
// Merge in defaults.
|
||||
if (empty($options['langcode'])) {
|
||||
$options['langcode'] = $this->defaultLangcode;
|
||||
}
|
||||
if (empty($options['context'])) {
|
||||
$options['context'] = '';
|
||||
}
|
||||
$translation = $this->getStringTranslation($options['langcode'], $string, $options['context']);
|
||||
|
||||
if($translation) {
|
||||
$this->translated[$string] = $translation;
|
||||
} else {
|
||||
$this->untranslated[$string] = $string;
|
||||
}
|
||||
|
||||
return $translation === FALSE ? $string : $translation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getTranslated() {
|
||||
return $this->translated;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getUntranslated() {
|
||||
return $this->untranslated;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Theme;
|
||||
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Theme\ThemeNegotiator;
|
||||
|
||||
/**
|
||||
* Class ThemeNegotiatorWrapper
|
||||
*/
|
||||
class ThemeNegotiatorWrapper extends ThemeNegotiator {
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Theme\ThemeNegotiatorInterface
|
||||
*/
|
||||
private $negotiator;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function determineActiveTheme(RouteMatchInterface $route_match) {
|
||||
foreach ($this->getSortedNegotiators() as $negotiator) {
|
||||
if ($negotiator->applies($route_match)) {
|
||||
$theme = $negotiator->determineActiveTheme($route_match);
|
||||
if ($theme !== NULL && $this->themeAccess->checkAccess($theme)) {
|
||||
$this->negotiator = $negotiator;
|
||||
return $theme;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Drupal\Core\Theme\ThemeNegotiatorInterface
|
||||
*/
|
||||
public function getNegotiator() {
|
||||
return $this->negotiator;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Twig\Dumper;
|
||||
|
||||
/**
|
||||
* Class HtmlDumper
|
||||
*/
|
||||
class HtmlDumper extends \Twig_Profiler_Dumper_Text {
|
||||
|
||||
private static $colors = array(
|
||||
'block' => '#dfd',
|
||||
'macro' => '#ddf',
|
||||
'template' => '#ffd',
|
||||
'big' => '#d44',
|
||||
);
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function dump(\Twig_Profiler_Profile $profile) {
|
||||
return '<pre class="h--word-intact"><code class="code--json">' . parent::dump($profile) . '</code></pre>';
|
||||
}
|
||||
|
||||
protected function formatTemplate(\Twig_Profiler_Profile $profile, $prefix) {
|
||||
return sprintf('%s└ <span style="background-color: %s">%s</span>', $prefix, self::$colors['template'], $profile->getTemplate());
|
||||
}
|
||||
|
||||
protected function formatNonTemplate(\Twig_Profiler_Profile $profile, $prefix) {
|
||||
return sprintf('%s└ %s::%s(<span style="background-color: %s">%s</span>)', $prefix, $profile->getTemplate(), $profile->getType(), isset(self::$colors[$profile->getType()]) ? self::$colors[$profile->getType()] : 'auto', $profile->getName());
|
||||
}
|
||||
|
||||
protected function formatTime(\Twig_Profiler_Profile $profile, $percent) {
|
||||
return sprintf('<span style="color: %s">%.2fms/%.0f%%</span>', $percent > 20 ? self::$colors['big'] : 'auto', $profile->getDuration() * 1000, $percent);
|
||||
}
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Twig\Extension;
|
||||
|
||||
use Drupal\webprofiler\Helper\ClassShortenerInterface;
|
||||
use Drupal\webprofiler\Helper\IdeLinkGeneratorInterface;
|
||||
use Symfony\Component\Stopwatch\Stopwatch;
|
||||
|
||||
/**
|
||||
* Class ProfilerExtension
|
||||
*/
|
||||
class ProfilerExtension extends \Twig_Extension_Profiler {
|
||||
|
||||
/**
|
||||
* @var \Symfony\Component\Stopwatch\Stopwatch
|
||||
*/
|
||||
private $stopwatch;
|
||||
private $events;
|
||||
private $ideLink;
|
||||
private $classShortener;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(\Twig_Profiler_Profile $profile, Stopwatch $stopwatch = NULL, IdeLinkGeneratorInterface $ideLink, ClassShortenerInterface $classShortener) {
|
||||
parent::__construct($profile);
|
||||
|
||||
$this->ideLink = $ideLink;
|
||||
$this->classShortener = $classShortener;
|
||||
|
||||
$this->stopwatch = $stopwatch;
|
||||
$this->events = new \SplObjectStorage();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function enter(\Twig_Profiler_Profile $profile) {
|
||||
if ($this->stopwatch && $profile->isTemplate()) {
|
||||
$this->events[$profile] = $this->stopwatch->start($profile->getName(), 'template');
|
||||
}
|
||||
|
||||
parent::enter($profile);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function leave(\Twig_Profiler_Profile $profile) {
|
||||
parent::leave($profile);
|
||||
|
||||
if ($this->stopwatch && $profile->isTemplate()) {
|
||||
$this->events[$profile]->stop();
|
||||
unset($this->events[$profile]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFunctions() {
|
||||
return [
|
||||
new \Twig_SimpleFunction('abbr', [$this, 'getAbbr']),
|
||||
new \Twig_SimpleFunction('idelink', [$this, 'getIdeLink']),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName() {
|
||||
return 'native_profiler';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $class
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAbbr($class) {
|
||||
return $this->classShortener->shortenClass($class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $file
|
||||
* @param $line
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIdeLink($file, $line) {
|
||||
return $this->ideLink->generateLink($file, $line);
|
||||
}
|
||||
}
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\webprofiler\Views;
|
||||
|
||||
use Drupal\views\ViewExecutable;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Class TraceableViewExecutable
|
||||
*/
|
||||
class TraceableViewExecutable extends ViewExecutable {
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
protected $render_time;
|
||||
|
||||
/**
|
||||
* Gets the build time.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getBuildTime() {
|
||||
return $this->build_time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the execute time.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getExecuteTime() {
|
||||
return $this->execute_time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the render time.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getRenderTime() {
|
||||
return $this->render_time;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function render($display_id = NULL) {
|
||||
$start = microtime(TRUE);
|
||||
|
||||
$this->execute($display_id);
|
||||
|
||||
// Check to see if the build failed.
|
||||
if (!empty($this->build_info['fail'])) {
|
||||
return;
|
||||
}
|
||||
if (!empty($this->build_info['denied'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$exposed_form = $this->display_handler->getPlugin('exposed_form');
|
||||
$exposed_form->preRender($this->result);
|
||||
|
||||
$module_handler = \Drupal::moduleHandler();
|
||||
|
||||
// @TODO In the longrun, it would be great to execute a view without
|
||||
// the theme system at all. See https://www.drupal.org/node/2322623.
|
||||
$active_theme = \Drupal::theme()->getActiveTheme();
|
||||
$themes = array_keys($active_theme->getBaseThemes());
|
||||
$themes[] = $active_theme->getName();
|
||||
|
||||
// Check for already-cached output.
|
||||
if (!empty($this->live_preview)) {
|
||||
$cache = FALSE;
|
||||
}
|
||||
else {
|
||||
/** @var \Drupal\views\Plugin\views\cache\CachePluginBase $cache */
|
||||
$cache = $this->display_handler->getPlugin('cache');
|
||||
}
|
||||
|
||||
// Run preRender for the pager as it might change the result.
|
||||
if (!empty($this->pager)) {
|
||||
$this->pager->preRender($this->result);
|
||||
}
|
||||
|
||||
// Initialize the style plugin.
|
||||
$this->initStyle();
|
||||
|
||||
if (!isset($this->response)) {
|
||||
// Set the response so other parts can alter it.
|
||||
$this->response = new Response('', 200);
|
||||
}
|
||||
|
||||
// Give field handlers the opportunity to perform additional queries
|
||||
// using the entire resultset prior to rendering.
|
||||
if ($this->style_plugin->usesFields()) {
|
||||
foreach ($this->field as $id => $handler) {
|
||||
if (!empty($this->field[$id])) {
|
||||
$this->field[$id]->preRender($this->result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->style_plugin->preRender($this->result);
|
||||
|
||||
// Let each area handler have access to the result set.
|
||||
$areas = ['header', 'footer'];
|
||||
// Only call preRender() on the empty handlers if the result is empty.
|
||||
if (empty($this->result)) {
|
||||
$areas[] = 'empty';
|
||||
}
|
||||
foreach ($areas as $area) {
|
||||
foreach ($this->{$area} as $handler) {
|
||||
$handler->preRender($this->result);
|
||||
}
|
||||
}
|
||||
|
||||
// Let modules modify the view just prior to rendering it.
|
||||
$module_handler->invokeAll('views_pre_render', [$this]);
|
||||
|
||||
// Let the themes play too, because pre render is a very themey thing.
|
||||
foreach ($themes as $theme_name) {
|
||||
$function = $theme_name . '_views_pre_render';
|
||||
if (function_exists($function)) {
|
||||
$function($this);
|
||||
}
|
||||
}
|
||||
|
||||
$this->display_handler->output = $this->display_handler->render();
|
||||
|
||||
$exposed_form->postRender($this->display_handler->output);
|
||||
|
||||
if ($cache) {
|
||||
$cache->postRender($this->display_handler->output);
|
||||
}
|
||||
|
||||
// Let modules modify the view output after it is rendered.
|
||||
$module_handler->invokeAll('views_post_render', [
|
||||
$this,
|
||||
&$this->display_handler->output,
|
||||
$cache
|
||||
]);
|
||||
|
||||
// Let the themes play too, because post render is a very themey thing.
|
||||
foreach ($themes as $theme_name) {
|
||||
$function = $theme_name . '_views_post_render';
|
||||
if (function_exists($function)) {
|
||||
$function($this);
|
||||
}
|
||||
}
|
||||
|
||||
$this->render_time = microtime(TRUE) - $start;
|
||||
|
||||
return $this->display_handler->output;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user