mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-26 03:58:45 +02:00
migration symfony 5 4 (#300)
* symfony 5.4 (diff dev) * symfony 5.4 (working) * symfony 5.4 (update autoload) * symfony 5.4 (remove swiftmailer mailer implementation) * symfony 5.4 (php doc and split Global accessor class) ### Impacted packages: composer require php:">=7.2.5 <8.0.0" symfony/console:5.4.* symfony/dotenv:5.4.* symfony/framework-bundle:5.4.* symfony/twig-bundle:5.4.* symfony/yaml:5.4.* --update-with-dependencies composer require symfony/stopwatch:5.4.* symfony/web-profiler-bundle:5.4.* --dev --update-with-dependencies
This commit is contained in:
@@ -2,23 +2,11 @@
|
||||
|
||||
{% import _self as helper %}
|
||||
|
||||
{% if colors is not defined %}
|
||||
{% set colors = {
|
||||
'default': '#999',
|
||||
'section': '#444',
|
||||
'event_listener': '#00B8F5',
|
||||
'template': '#66CC00',
|
||||
'doctrine': '#FF6633',
|
||||
} %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% block toolbar %}
|
||||
{% set has_time_events = collector.events|length > 0 %}
|
||||
|
||||
{% set total_time = has_time_events ? '%.0f'|format(collector.duration) : 'n/a' %}
|
||||
{% set initialization_time = collector.events|length ? '%.0f'|format(collector.inittime) : 'n/a' %}
|
||||
{% set status_color = has_time_events and collector.duration > 1000 ? 'yellow' : '' %}
|
||||
{% set status_color = has_time_events and collector.duration > 1000 ? 'yellow' %}
|
||||
|
||||
{% set icon %}
|
||||
{{ include('@WebProfiler/Icon/time.svg') }}
|
||||
@@ -101,7 +89,7 @@
|
||||
</div>
|
||||
{% elseif collector.events is empty %}
|
||||
<div class="empty">
|
||||
<p>No timing events have been recorded. Are you sure that debugging is enabled in the kernel?</p>
|
||||
<p>No timing events have been recorded. Check that symfony/stopwatch is installed and debugging enabled in the kernel.</p>
|
||||
</div>
|
||||
{% else %}
|
||||
{{ block('panelContent') }}
|
||||
@@ -112,7 +100,7 @@
|
||||
<form id="timeline-control" action="" method="get">
|
||||
<input type="hidden" name="panel" value="time">
|
||||
<label for="threshold">Threshold</label>
|
||||
<input type="number" size="3" name="threshold" id="threshold" value="3" min="0"> ms
|
||||
<input type="number" name="threshold" id="threshold" value="1" min="0" placeholder="1.1"> ms
|
||||
<span class="help">(timeline only displays events with a duration longer than this threshold)</span>
|
||||
</form>
|
||||
|
||||
@@ -130,7 +118,7 @@
|
||||
</h3>
|
||||
{% endif %}
|
||||
|
||||
{{ helper.display_timeline('timeline_' ~ token, collector.events, colors) }}
|
||||
{{ helper.display_timeline(token, collector.events, collector.events.__section__.origin) }}
|
||||
|
||||
{% if profile.children|length %}
|
||||
<p class="help">Note: sections with a striped background correspond to sub-requests.</p>
|
||||
@@ -144,380 +132,34 @@
|
||||
<small>{{ events.__section__.duration }} ms</small>
|
||||
</h4>
|
||||
|
||||
{{ helper.display_timeline('timeline_' ~ child.token, events, colors) }}
|
||||
{{ helper.display_timeline(child.token, events, collector.events.__section__.origin) }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<script>{% autoescape 'js' %}//<![CDATA[
|
||||
/**
|
||||
* In-memory key-value cache manager
|
||||
*/
|
||||
var cache = new function() {
|
||||
"use strict";
|
||||
var dict = {};
|
||||
|
||||
this.get = function(key) {
|
||||
return dict.hasOwnProperty(key)
|
||||
? dict[key]
|
||||
: null;
|
||||
};
|
||||
|
||||
this.set = function(key, value) {
|
||||
dict[key] = value;
|
||||
|
||||
return value;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Query an element with a CSS selector.
|
||||
*
|
||||
* @param {string} selector - a CSS-selector-compatible query string
|
||||
*
|
||||
* @return DOMElement|null
|
||||
*/
|
||||
function query(selector)
|
||||
{
|
||||
"use strict";
|
||||
var key = 'SELECTOR: ' + selector;
|
||||
|
||||
return cache.get(key) || cache.set(key, document.querySelector(selector));
|
||||
}
|
||||
|
||||
/**
|
||||
* Canvas Manager
|
||||
*/
|
||||
function CanvasManager(requests, maxRequestTime) {
|
||||
"use strict";
|
||||
|
||||
var _drawingColors = {{ colors|json_encode|raw }},
|
||||
_storagePrefix = 'timeline/',
|
||||
_threshold = 1,
|
||||
_requests = requests,
|
||||
_maxRequestTime = maxRequestTime;
|
||||
|
||||
/**
|
||||
* Check whether this event is a child event.
|
||||
*
|
||||
* @return true if it is
|
||||
*/
|
||||
function isChildEvent(event)
|
||||
{
|
||||
return '__section__.child' === event.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this event is categorized in 'section'.
|
||||
*
|
||||
* @return true if it is
|
||||
*/
|
||||
function isSectionEvent(event)
|
||||
{
|
||||
return 'section' === event.category;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the width of the container.
|
||||
*/
|
||||
function getContainerWidth()
|
||||
{
|
||||
return query('#collector-content h2').clientWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw one canvas.
|
||||
*
|
||||
* @param request the request object
|
||||
* @param max <subjected for removal>
|
||||
* @param threshold the threshold (lower bound) of the length of the timeline (in milliseconds)
|
||||
* @param width the width of the canvas
|
||||
*/
|
||||
this.drawOne = function(request, max, threshold, width)
|
||||
{
|
||||
"use strict";
|
||||
var text,
|
||||
ms,
|
||||
xc,
|
||||
drawableEvents,
|
||||
mainEvents,
|
||||
elementId = 'timeline_' + request.id,
|
||||
canvasHeight = 0,
|
||||
gapPerEvent = 38,
|
||||
colors = _drawingColors,
|
||||
space = 10.5,
|
||||
ratio = (width - space * 2) / max,
|
||||
h = space,
|
||||
x = request.left * ratio + space, // position
|
||||
canvas = cache.get(elementId) || cache.set(elementId, document.getElementById(elementId)),
|
||||
ctx = canvas.getContext("2d"),
|
||||
scaleRatio,
|
||||
devicePixelRatio;
|
||||
|
||||
// Filter events whose total time is below the threshold.
|
||||
drawableEvents = request.events.filter(function(event) {
|
||||
return event.duration >= threshold;
|
||||
});
|
||||
|
||||
canvasHeight += gapPerEvent * drawableEvents.length;
|
||||
|
||||
// For retina displays so text and boxes will be crisp
|
||||
devicePixelRatio = window.devicePixelRatio == "undefined" ? 1 : window.devicePixelRatio;
|
||||
scaleRatio = devicePixelRatio / 1;
|
||||
|
||||
canvas.width = width * scaleRatio;
|
||||
canvas.height = canvasHeight * scaleRatio;
|
||||
|
||||
canvas.style.width = width + 'px';
|
||||
canvas.style.height = canvasHeight + 'px';
|
||||
|
||||
ctx.scale(scaleRatio, scaleRatio);
|
||||
|
||||
ctx.textBaseline = "middle";
|
||||
ctx.lineWidth = 0;
|
||||
|
||||
// For each event, draw a line.
|
||||
ctx.strokeStyle = "#CCC";
|
||||
|
||||
drawableEvents.forEach(function(event) {
|
||||
event.periods.forEach(function(period) {
|
||||
var timelineHeadPosition = x + period.start * ratio;
|
||||
|
||||
if (isChildEvent(event)) {
|
||||
/* create a striped background dynamically */
|
||||
var img = new Image();
|
||||
img.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKBAMAAAB/HNKOAAAAIVBMVEX////w8PDd7h7d7h7d7h7d7h7w8PDw8PDw8PDw8PDw8PAOi84XAAAAKUlEQVQImWNI71zAwMBQMYuBgY0BxExnADErGEDMTgYQE8hnAKtCZwIAlcMNSR9a1OEAAAAASUVORK5CYII=';
|
||||
var pattern = ctx.createPattern(img, 'repeat');
|
||||
|
||||
ctx.fillStyle = pattern;
|
||||
ctx.fillRect(timelineHeadPosition, 0, (period.end - period.start) * ratio, canvasHeight);
|
||||
} else if (isSectionEvent(event)) {
|
||||
var timelineTailPosition = x + period.end * ratio;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(timelineHeadPosition, 0);
|
||||
ctx.lineTo(timelineHeadPosition, canvasHeight);
|
||||
ctx.moveTo(timelineTailPosition, 0);
|
||||
ctx.lineTo(timelineTailPosition, canvasHeight);
|
||||
ctx.fill();
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Filter for main events.
|
||||
mainEvents = drawableEvents.filter(function(event) {
|
||||
return !isChildEvent(event)
|
||||
});
|
||||
|
||||
// For each main event, draw the visual presentation of timelines.
|
||||
mainEvents.forEach(function(event) {
|
||||
|
||||
h += 8;
|
||||
|
||||
// For each sub event, ...
|
||||
event.periods.forEach(function(period) {
|
||||
// Set the drawing style.
|
||||
ctx.fillStyle = colors['default'];
|
||||
ctx.strokeStyle = colors['default'];
|
||||
|
||||
if (colors[event.name]) {
|
||||
ctx.fillStyle = colors[event.name];
|
||||
ctx.strokeStyle = colors[event.name];
|
||||
} else if (colors[event.category]) {
|
||||
ctx.fillStyle = colors[event.category];
|
||||
ctx.strokeStyle = colors[event.category];
|
||||
}
|
||||
|
||||
// Draw the timeline
|
||||
var timelineHeadPosition = x + period.start * ratio;
|
||||
|
||||
if (!isSectionEvent(event)) {
|
||||
ctx.fillRect(timelineHeadPosition, h + 3, 2, 8);
|
||||
ctx.fillRect(timelineHeadPosition, h, (period.end - period.start) * ratio || 2, 6);
|
||||
} else {
|
||||
var timelineTailPosition = x + period.end * ratio;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(timelineHeadPosition, h);
|
||||
ctx.lineTo(timelineHeadPosition, h + 11);
|
||||
ctx.lineTo(timelineHeadPosition + 8, h);
|
||||
ctx.lineTo(timelineHeadPosition, h);
|
||||
ctx.fill();
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(timelineTailPosition, h);
|
||||
ctx.lineTo(timelineTailPosition, h + 11);
|
||||
ctx.lineTo(timelineTailPosition - 8, h);
|
||||
ctx.lineTo(timelineTailPosition, h);
|
||||
ctx.fill();
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(timelineHeadPosition, h);
|
||||
ctx.lineTo(timelineTailPosition, h);
|
||||
ctx.lineTo(timelineTailPosition, h + 2);
|
||||
ctx.lineTo(timelineHeadPosition, h + 2);
|
||||
ctx.lineTo(timelineHeadPosition, h);
|
||||
ctx.fill();
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
}
|
||||
});
|
||||
|
||||
h += 30;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.strokeStyle = "#E0E0E0";
|
||||
ctx.moveTo(0, h - 10);
|
||||
ctx.lineTo(width, h - 10);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
});
|
||||
|
||||
h = space;
|
||||
|
||||
// For each event, draw the label.
|
||||
mainEvents.forEach(function(event) {
|
||||
|
||||
ctx.fillStyle = "#444";
|
||||
ctx.font = "12px sans-serif";
|
||||
text = event.name;
|
||||
ms = " " + (event.duration < 1 ? event.duration : parseInt(event.duration, 10)) + " ms / " + event.memory + " MiB";
|
||||
if (x + event.starttime * ratio + ctx.measureText(text + ms).width > width) {
|
||||
ctx.textAlign = "end";
|
||||
ctx.font = "10px sans-serif";
|
||||
ctx.fillStyle = "#777";
|
||||
xc = x + event.endtime * ratio - 1;
|
||||
ctx.fillText(ms, xc, h);
|
||||
|
||||
xc -= ctx.measureText(ms).width;
|
||||
ctx.font = "12px sans-serif";
|
||||
ctx.fillStyle = "#222";
|
||||
ctx.fillText(text, xc, h);
|
||||
} else {
|
||||
ctx.textAlign = "start";
|
||||
ctx.font = "13px sans-serif";
|
||||
ctx.fillStyle = "#222";
|
||||
xc = x + event.starttime * ratio + 1;
|
||||
ctx.fillText(text, xc, h);
|
||||
|
||||
xc += ctx.measureText(text).width;
|
||||
ctx.font = "11px sans-serif";
|
||||
ctx.fillStyle = "#777";
|
||||
ctx.fillText(ms, xc, h);
|
||||
}
|
||||
|
||||
h += gapPerEvent;
|
||||
});
|
||||
};
|
||||
|
||||
this.drawAll = function(width, threshold)
|
||||
{
|
||||
"use strict";
|
||||
|
||||
width = width || getContainerWidth();
|
||||
threshold = threshold || this.getThreshold();
|
||||
|
||||
var self = this;
|
||||
|
||||
_requests.forEach(function(request) {
|
||||
self.drawOne(request, _maxRequestTime, threshold, width);
|
||||
});
|
||||
};
|
||||
|
||||
this.getThreshold = function() {
|
||||
var threshold = Sfjs.getPreference(_storagePrefix + 'threshold');
|
||||
|
||||
if (null === threshold) {
|
||||
return _threshold;
|
||||
}
|
||||
|
||||
_threshold = parseInt(threshold);
|
||||
|
||||
return _threshold;
|
||||
};
|
||||
|
||||
this.setThreshold = function(threshold)
|
||||
{
|
||||
_threshold = threshold;
|
||||
|
||||
Sfjs.setPreference(_storagePrefix + 'threshold', threshold);
|
||||
|
||||
return this;
|
||||
};
|
||||
}
|
||||
|
||||
function canvasAutoUpdateOnResizeAndSubmit(e) {
|
||||
e.preventDefault();
|
||||
canvasManager.drawAll();
|
||||
}
|
||||
|
||||
function canvasAutoUpdateOnThresholdChange(e) {
|
||||
canvasManager
|
||||
.setThreshold(query('input[name="threshold"]').value)
|
||||
.drawAll();
|
||||
}
|
||||
|
||||
var requests_data = {
|
||||
"max": {{ "%F"|format(collector.events.__section__.endtime) }},
|
||||
"requests": [
|
||||
{{ helper.dump_request_data(token, profile, collector.events, collector.events.__section__.origin) }}
|
||||
|
||||
{% if profile.children|length %}
|
||||
,
|
||||
{% for child in profile.children %}
|
||||
{{ helper.dump_request_data(child.token, child, child.getcollector('time').events, collector.events.__section__.origin) }}{{ loop.last ? '' : ',' }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
]
|
||||
};
|
||||
|
||||
var canvasManager = new CanvasManager(requests_data.requests, requests_data.max);
|
||||
|
||||
query('input[name="threshold"]').value = canvasManager.getThreshold();
|
||||
canvasManager.drawAll();
|
||||
|
||||
// Update the colors of legends.
|
||||
var timelineLegends = document.querySelectorAll('.sf-profiler-timeline > .legends > span[data-color]');
|
||||
|
||||
for (var i = 0; i < timelineLegends.length; ++i) {
|
||||
var timelineLegend = timelineLegends[i];
|
||||
|
||||
timelineLegend.style.borderLeftColor = timelineLegend.getAttribute('data-color');
|
||||
}
|
||||
|
||||
// Bind event handlers
|
||||
var elementTimelineControl = query('#timeline-control'),
|
||||
elementThresholdControl = query('input[name="threshold"]');
|
||||
|
||||
window.onresize = canvasAutoUpdateOnResizeAndSubmit;
|
||||
elementTimelineControl.onsubmit = canvasAutoUpdateOnResizeAndSubmit;
|
||||
|
||||
elementThresholdControl.onclick = canvasAutoUpdateOnThresholdChange;
|
||||
elementThresholdControl.onchange = canvasAutoUpdateOnThresholdChange;
|
||||
elementThresholdControl.onkeyup = canvasAutoUpdateOnThresholdChange;
|
||||
|
||||
window.setTimeout(function() {
|
||||
canvasAutoUpdateOnThresholdChange(null);
|
||||
}, 50);
|
||||
|
||||
//]]>{% endautoescape %}</script>
|
||||
<svg id="timeline-template" width="0" height="0">
|
||||
<defs>
|
||||
<pattern id="subrequest" class="timeline-subrequest-pattern" patternUnits="userSpaceOnUse" width="20" height="20" viewBox="0 0 40 40">
|
||||
<path d="M0 40L40 0H20L0 20M40 40V20L20 40"/>
|
||||
</pattern>
|
||||
</defs>
|
||||
</svg>
|
||||
<style type="text/css">
|
||||
{% include '@WebProfiler/Collector/time.css.twig' %}
|
||||
</style>
|
||||
<script>
|
||||
{% include '@WebProfiler/Collector/time.js' %}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% macro dump_request_data(token, profile, events, origin) %}
|
||||
{% macro dump_request_data(token, events, origin) %}
|
||||
{% autoescape 'js' %}
|
||||
{% from _self import dump_events %}
|
||||
{
|
||||
"id": "{{ token }}",
|
||||
"left": {{ "%F"|format(events.__section__.origin - origin) }},
|
||||
"events": [
|
||||
{{ dump_events(events) }}
|
||||
]
|
||||
}
|
||||
{
|
||||
id: "{{ token }}",
|
||||
left: {{ "%F"|format(events.__section__.origin - origin) }},
|
||||
end: "{{ '%F'|format(events.__section__.endtime) }}",
|
||||
events: [ {{ dump_events(events) }} ],
|
||||
}
|
||||
{% endautoescape %}
|
||||
{% endmacro %}
|
||||
|
||||
@@ -525,32 +167,48 @@
|
||||
{% autoescape 'js' %}
|
||||
{% for name, event in events %}
|
||||
{% if '__section__' != name %}
|
||||
{
|
||||
"name": "{{ name }}",
|
||||
"category": "{{ event.category }}",
|
||||
"origin": {{ "%F"|format(event.origin) }},
|
||||
"starttime": {{ "%F"|format(event.starttime) }},
|
||||
"endtime": {{ "%F"|format(event.endtime) }},
|
||||
"duration": {{ "%F"|format(event.duration) }},
|
||||
"memory": {{ "%.1F"|format(event.memory / 1024 / 1024) }},
|
||||
"periods": [
|
||||
{%- for period in event.periods -%}
|
||||
{"start": {{ "%F"|format(period.starttime) }}, "end": {{ "%F"|format(period.endtime) }}}{{ loop.last ? '' : ', ' }}
|
||||
{%- endfor -%}
|
||||
]
|
||||
}{{ loop.last ? '' : ',' }}
|
||||
{
|
||||
name: "{{ name }}",
|
||||
category: "{{ event.category }}",
|
||||
origin: {{ "%F"|format(event.origin) }},
|
||||
starttime: {{ "%F"|format(event.starttime) }},
|
||||
endtime: {{ "%F"|format(event.endtime) }},
|
||||
duration: {{ "%F"|format(event.duration) }},
|
||||
memory: {{ "%.1F"|format(event.memory / 1024 / 1024) }},
|
||||
elements: {},
|
||||
periods: [
|
||||
{%- for period in event.periods -%}
|
||||
{
|
||||
start: {{ "%F"|format(period.starttime) }},
|
||||
end: {{ "%F"|format(period.endtime) }},
|
||||
duration: {{ "%F"|format(period.duration) }},
|
||||
elements: {}
|
||||
},
|
||||
{%- endfor -%}
|
||||
],
|
||||
},
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endautoescape %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro display_timeline(id, events, colors) %}
|
||||
{% macro display_timeline(token, events, origin) %}
|
||||
{% import _self as helper %}
|
||||
<div class="sf-profiler-timeline">
|
||||
<div class="legends">
|
||||
{% for category, color in colors %}
|
||||
<span data-color="{{ color }}">{{ category }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<canvas width="680" height="" id="{{ id }}" class="timeline"></canvas>
|
||||
<div id="legend-{{ token }}" class="legends"></div>
|
||||
<svg id="timeline-{{ token }}" class="timeline-graph"></svg>
|
||||
<script>{% autoescape 'js' %}
|
||||
window.addEventListener('load', function onLoad() {
|
||||
const theme = new Theme();
|
||||
|
||||
new TimelineEngine(
|
||||
theme,
|
||||
new SvgRenderer(document.getElementById('timeline-{{ token }}')),
|
||||
new Legend(document.getElementById('legend-{{ token }}'), theme),
|
||||
document.getElementById('threshold'),
|
||||
{{ helper.dump_request_data(token, events, origin) }}
|
||||
);
|
||||
});
|
||||
{% endautoescape %}</script>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
Reference in New Issue
Block a user