4 |
5 | */
6 |
7 | div.phpdebugbar .hljs {
8 | display: block; padding: 0.5em;
9 | color: #333;
10 | background: #f8f8f8
11 | }
12 |
13 | div.phpdebugbar .hljs-comment,
14 | div.phpdebugbar .hljs-template_comment,
15 | div.phpdebugbar .diff .hljs-header,
16 | div.phpdebugbar .hljs-javadoc {
17 | color: #998;
18 | font-style: italic
19 | }
20 |
21 | div.phpdebugbar .hljs-keyword,
22 | div.phpdebugbar .css .rule .hljs-keyword,
23 | div.phpdebugbar .hljs-winutils,
24 | div.phpdebugbar .javascript .hljs-title,
25 | div.phpdebugbar .nginx .hljs-title,
26 | div.phpdebugbar .hljs-subst,
27 | div.phpdebugbar .hljs-request,
28 | div.phpdebugbar .hljs-status {
29 | color: #333;
30 | font-weight: bold
31 | }
32 |
33 | div.phpdebugbar .hljs-number,
34 | div.phpdebugbar .hljs-hexcolor,
35 | div.phpdebugbar .ruby .hljs-constant {
36 | color: #099;
37 | }
38 |
39 | div.phpdebugbar .hljs-string,
40 | div.phpdebugbar .hljs-tag .hljs-value,
41 | div.phpdebugbar .hljs-phpdoc,
42 | div.phpdebugbar .tex .hljs-formula {
43 | color: #d14
44 | }
45 |
46 | div.phpdebugbar .hljs-title,
47 | div.phpdebugbar .hljs-id,
48 | div.phpdebugbar .coffeescript .hljs-params,
49 | div.phpdebugbar .scss .hljs-preprocessor {
50 | color: #900;
51 | font-weight: bold
52 | }
53 |
54 | div.phpdebugbar .javascript .hljs-title,
55 | div.phpdebugbar .lisp .hljs-title,
56 | div.phpdebugbar .clojure .hljs-title,
57 | div.phpdebugbar .hljs-subst {
58 | font-weight: normal
59 | }
60 |
61 | div.phpdebugbar .hljs-class .hljs-title,
62 | div.phpdebugbar .haskell .hljs-type,
63 | div.phpdebugbar .vhdl .hljs-literal,
64 | div.phpdebugbar .tex .hljs-command {
65 | color: #458;
66 | font-weight: bold
67 | }
68 |
69 | div.phpdebugbar .hljs-tag,
70 | div.phpdebugbar .hljs-tag .hljs-title,
71 | div.phpdebugbar .hljs-rules .hljs-property,
72 | div.phpdebugbar .django .hljs-tag .hljs-keyword {
73 | color: #000080;
74 | font-weight: normal
75 | }
76 |
77 | div.phpdebugbar .hljs-attribute,
78 | div.phpdebugbar .hljs-variable,
79 | div.phpdebugbar .lisp .hljs-body {
80 | color: #008080
81 | }
82 |
83 | div.phpdebugbar .hljs-regexp {
84 | color: #009926
85 | }
86 |
87 | div.phpdebugbar .hljs-symbol,
88 | div.phpdebugbar .ruby .hljs-symbol .hljs-string,
89 | div.phpdebugbar .lisp .hljs-keyword,
90 | div.phpdebugbar .tex .hljs-special,
91 | div.phpdebugbar .hljs-prompt {
92 | color: #990073
93 | }
94 |
95 | div.phpdebugbar .hljs-built_in,
96 | div.phpdebugbar .lisp .hljs-title,
97 | div.phpdebugbar .clojure .hljs-built_in {
98 | color: #0086b3
99 | }
100 |
101 | div.phpdebugbar .hljs-preprocessor,
102 | div.phpdebugbar .hljs-pragma,
103 | div.phpdebugbar .hljs-pi,
104 | div.phpdebugbar .hljs-doctype,
105 | div.phpdebugbar .hljs-shebang,
106 | div.phpdebugbar .hljs-cdata {
107 | color: #999;
108 | font-weight: bold
109 | }
110 |
111 | div.phpdebugbar .hljs-deletion {
112 | background: #fdd
113 | }
114 |
115 | div.phpdebugbar .hljs-addition {
116 | background: #dfd
117 | }
118 |
119 | div.phpdebugbar .diff .hljs-change {
120 | background: #0086b3
121 | }
122 |
123 | div.phpdebugbar .hljs-chunk {
124 | color: #aaa
125 | }
126 |
--------------------------------------------------------------------------------
/assets/widgets.css:
--------------------------------------------------------------------------------
1 | pre.phpdebugbar-widgets-code-block {
2 | white-space: pre;
3 | word-wrap: normal;
4 | overflow: hidden;
5 | }
6 | pre.phpdebugbar-widgets-code-block code {
7 | display: block;
8 | overflow-x: auto;
9 | overflow-y: hidden;
10 | }
11 | pre.phpdebugbar-widgets-code-block code.phpdebugbar-widgets-numbered-code {
12 | padding: 5px;
13 | }
14 | pre.phpdebugbar-widgets-code-block code span.phpdebugbar-widgets-highlighted-line {
15 | background: #800000;
16 | color: #fff;
17 | display: inline-block;
18 | min-width: 100%;
19 | }
20 | pre.phpdebugbar-widgets-code-block code span.phpdebugbar-widgets-highlighted-line span {
21 | background: none !important;
22 | color: inherit !important;
23 | }
24 | pre.phpdebugbar-widgets-code-block ul {
25 | float: left;
26 | padding: 5px;
27 | background: #cacaca;
28 | border-right: 1px solid #aaa;
29 | text-align: right;
30 | }
31 |
32 | /* -------------------------------------- */
33 |
34 | ul.phpdebugbar-widgets-list {
35 | margin: 0;
36 | padding: 0;
37 | list-style: none;
38 | font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
39 | }
40 | ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item {
41 | padding: 3px 6px;
42 | border-bottom: 1px solid #eee;
43 | position: relative;
44 | overflow: hidden;
45 | }
46 | ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item:hover {
47 | background: #fafafa;
48 | }
49 |
50 | /* -------------------------------------- */
51 |
52 | div.phpdebugbar-widgets-messages {
53 | position: relative;
54 | height: 100%;
55 | }
56 | div.phpdebugbar-widgets-messages ul.phpdebugbar-widgets-list {
57 | padding-bottom: 20px;
58 | }
59 | div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value.phpdebugbar-widgets-warning:before {
60 | font-family: PhpDebugbarFontAwesome;
61 | content: "\f071";
62 | margin-right: 8px;
63 | font-size: 11px;
64 | color: #ecb03d;
65 | }
66 | div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value.phpdebugbar-widgets-error {
67 | color: red;
68 | }
69 | div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value.phpdebugbar-widgets-error:before {
70 | font-family: PhpDebugbarFontAwesome;
71 | content: "\f057";
72 | margin-right: 8px;
73 | font-size: 11px;
74 | color: red;
75 | }
76 | div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item pre.sf-dump {
77 | display: inline;
78 | }
79 | div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-collector,
80 | div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-label {
81 | float: right;
82 | font-size: 12px;
83 | padding: 2px 4px;
84 | color: #888;
85 | margin: 0 2px;
86 | text-decoration: none;
87 | text-shadow: none;
88 | background: none;
89 | font-weight: normal;
90 | }
91 | div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-collector {
92 | color: #555;
93 | font-style: italic;
94 | }
95 | div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar {
96 | position: fixed;
97 | bottom: 0;
98 | width: 100%;
99 | background: #fff;
100 | }
101 | div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar input {
102 | border: 0;
103 | margin: 0;
104 | margin-left: 7px;
105 | width: 50%;
106 | box-shadow: none;
107 | }
108 | div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar input:focus {
109 | outline: none;
110 | }
111 | div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter {
112 | float: right;
113 | font-size: 12px;
114 | padding: 2px 4px;
115 | background: #7cacd5;
116 | margin: 0 2px;
117 | border-radius: 4px;
118 | color: #fff;
119 | text-decoration: none;
120 | }
121 | div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter.phpdebugbar-widgets-excluded {
122 | background: #eee;
123 | color: #888;
124 | }
125 |
126 | /* -------------------------------------- */
127 |
128 | dl.phpdebugbar-widgets-kvlist {
129 | margin: 0;
130 | }
131 | dl.phpdebugbar-widgets-kvlist dt {
132 | float: left;
133 | width: 150px;
134 | padding: 5px;
135 | border-top: 1px solid #eee;
136 | font-weight: bold;
137 | clear: both;
138 | overflow: hidden;
139 | text-overflow: ellipsis;
140 | white-space: nowrap;
141 | }
142 | dl.phpdebugbar-widgets-kvlist dd {
143 | margin-left: 160px;
144 | padding: 5px;
145 | border-top: 1px solid #eee;
146 | cursor: pointer;
147 | min-height: 17px;
148 | }
149 |
150 | /* -------------------------------------- */
151 |
152 | dl.phpdebugbar-widgets-varlist,
153 | dl.phpdebugbar-widgets-htmlvarlist {
154 | font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
155 | }
156 | dl.phpdebugbar-widgets-htmlvarlist dd {
157 | cursor: initial;
158 | }
159 |
160 | /* -------------------------------------- */
161 |
162 | ul.phpdebugbar-widgets-timeline {
163 | margin: 0;
164 | padding: 0;
165 | list-style: none;
166 | }
167 | ul.phpdebugbar-widgets-timeline .phpdebugbar-widgets-measure {
168 | height: 20px;
169 | position: relative;
170 | border-bottom: 1px solid #eee;
171 | display: block;
172 | }
173 | ul.phpdebugbar-widgets-timeline li:hover {
174 | background: #fafafa;
175 | }
176 | ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-label,
177 | ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-collector {
178 | position: absolute;
179 | font-size: 12px;
180 | font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
181 | color: #555;
182 | top: 4px;
183 | left: 5px;
184 | background: none;
185 | text-shadow: none;
186 | font-weight: normal;
187 | white-space: pre;
188 | }
189 | ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-collector {
190 | left: initial;
191 | right: 5px;
192 | }
193 | ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-value {
194 | display: block;
195 | position: absolute;
196 | height: 10px;
197 | background: #3db9ec;
198 | top: 5px;
199 | border-radius: 2px;
200 | min-width: 1px;
201 | }
202 | ul.phpdebugbar-widgets-timeline table.phpdebugbar-widgets-params {
203 | display: none;
204 | width: 70%;
205 | margin: 10px;
206 | border: 1px solid #ddd;
207 | font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
208 | border-collapse: collapse;
209 | }
210 | ul.phpdebugbar-widgets-timeline table.phpdebugbar-widgets-params td {
211 | border: 1px solid #ddd;
212 | padding: 0 5px;
213 | }
214 | ul.phpdebugbar-widgets-timeline table.phpdebugbar-widgets-params .phpdebugbar-widgets-name {
215 | width: 20%;
216 | font-weight: bold;
217 | }
218 |
219 | /* -------------------------------------- */
220 |
221 | div.phpdebugbar-widgets-exceptions li.phpdebugbar-widgets-list-item {
222 | cursor: pointer;
223 | }
224 | div.phpdebugbar-widgets-exceptions li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-message {
225 | display: block;
226 | color: red;
227 | }
228 |
229 | div.phpdebugbar-widgets-exceptions li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-filename {
230 | display: block;
231 | font-style: italic;
232 | color: #555;
233 | }
234 |
235 | div.phpdebugbar-widgets-exceptions li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-type {
236 | display: block;
237 | position: absolute;
238 | right: 4px;
239 | top: 4px;
240 | font-weight: bold;
241 | }
242 |
243 | div.phpdebugbar-widgets-exceptions li.phpdebugbar-widgets-list-item pre.phpdebugbar-widgets-file {
244 | display: none;
245 | margin: 10px;
246 | padding: 5px;
247 | border: 1px solid #ddd;
248 | font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
249 | }
250 |
251 | div.phpdebugbar-widgets-exceptions a.phpdebugbar-widgets-editor-link:before {
252 | font-family: PhpDebugbarFontAwesome;
253 | margin-right: 4px;
254 | font-size: 12px;
255 | font-style: normal;
256 | }
257 |
258 | div.phpdebugbar-widgets-exceptions a.phpdebugbar-widgets-editor-link:before {
259 | content: "\f08e";
260 | margin-left: 4px;
261 | }
262 |
--------------------------------------------------------------------------------
/assets/widgets/mails/widget.css:
--------------------------------------------------------------------------------
1 |
2 | div.phpdebugbar-widgets-mails span.phpdebugbar-widgets-subject {
3 | display: block;
4 | }
5 |
6 | div.phpdebugbar-widgets-mails li.phpdebugbar-widgets-list-item pre.phpdebugbar-widgets-headers {
7 | display: none;
8 | margin: 10px;
9 | padding: 5px;
10 | border: 1px solid #ddd;
11 | font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
12 | }
13 |
--------------------------------------------------------------------------------
/assets/widgets/mails/widget.js:
--------------------------------------------------------------------------------
1 | (function($) {
2 |
3 | var csscls = PhpDebugBar.utils.makecsscls('phpdebugbar-widgets-');
4 |
5 | /**
6 | * Widget for the displaying mails data
7 | *
8 | * Options:
9 | * - data
10 | */
11 | var MailsWidget = PhpDebugBar.Widgets.MailsWidget = PhpDebugBar.Widget.extend({
12 |
13 | className: csscls('mails'),
14 |
15 | render: function() {
16 | this.$list = new PhpDebugBar.Widgets.ListWidget({ itemRenderer: function(li, mail) {
17 | $('').addClass(csscls('subject')).text(mail.subject).appendTo(li);
18 | $('').addClass(csscls('to')).text(mail.to).appendTo(li);
19 | if (mail.headers) {
20 | var headers = $('').addClass(csscls('headers')).appendTo(li);
21 | $('
').text(mail.headers).appendTo(headers);
22 | li.click(function() {
23 | if (headers.is(':visible')) {
24 | headers.hide();
25 | } else {
26 | headers.show();
27 | }
28 | });
29 | }
30 | }});
31 | this.$list.$el.appendTo(this.$el);
32 |
33 | this.bindAttr('data', function(data) {
34 | this.$list.set('data', data);
35 | });
36 | }
37 |
38 | });
39 |
40 | })(PhpDebugBar.$);
41 |
--------------------------------------------------------------------------------
/assets/widgets/sqlqueries/widget.css:
--------------------------------------------------------------------------------
1 | div.phpdebugbar-widgets-sqlqueries .phpdebugbar-widgets-status {
2 | font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
3 | padding: 6px 6px;
4 | border-bottom: 1px solid #ddd;
5 | font-weight: bold;
6 | color: #555;
7 | background: #fafafa;
8 | }
9 |
10 | div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item.phpdebugbar-widgets-error {
11 | color: red;
12 | }
13 |
14 | div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-database,
15 | div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-duration,
16 | div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-memory,
17 | div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-row-count,
18 | div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-copy-clipboard,
19 | div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-stmt-id {
20 | float: right;
21 | margin-left: 8px;
22 | color: #888;
23 | }
24 | div.phpdebugbar-widgets-sqlqueries div.phpdebugbar-widgets-status span.phpdebugbar-widgets-database,
25 | div.phpdebugbar-widgets-sqlqueries div.phpdebugbar-widgets-status span.phpdebugbar-widgets-duration,
26 | div.phpdebugbar-widgets-sqlqueries div.phpdebugbar-widgets-status span.phpdebugbar-widgets-memory,
27 | div.phpdebugbar-widgets-sqlqueries div.phpdebugbar-widgets-status span.phpdebugbar-widgets-row-count,
28 | div.phpdebugbar-widgets-sqlqueries div.phpdebugbar-widgets-status span.phpdebugbar-widgets-copy-clipboard,
29 | div.phpdebugbar-widgets-sqlqueries div.phpdebugbar-widgets-status span.phpdebugbar-widgets-stmt-id {
30 | color: #555;
31 | }
32 | div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-database:before,
33 | div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-duration:before,
34 | div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-memory:before,
35 | div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-row-count:before,
36 | div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-copy-clipboard:before,
37 | div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-stmt-id:before {
38 | font-family: PhpDebugbarFontAwesome;
39 | margin-right: 4px;
40 | font-size: 12px;
41 | }
42 | div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-database:before {
43 | content: "\f1c0";
44 | }
45 | div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-duration:before {
46 | content: "\f017";
47 | }
48 | div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-memory:before {
49 | content: "\f085";
50 | }
51 | div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-row-count:before {
52 | content: "\f0ce";
53 | }
54 | div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-stmt-id:before {
55 | content: "\f08d";
56 | }
57 | div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-copy-clipboard:before {
58 | content: "\f0c5";
59 | }
60 | div.phpdebugbar-widgets-sqlqueries table.phpdebugbar-widgets-params {
61 | display: none;
62 | width: 70%;
63 | margin: 10px;
64 | border: 1px solid #ddd;
65 | font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
66 | border-collapse: collapse;
67 | }
68 | div.phpdebugbar-widgets-sqlqueries table.phpdebugbar-widgets-params td {
69 | border: 1px solid #ddd;
70 | text-align: center;
71 | }
72 | div.phpdebugbar-widgets-sqlqueries table.phpdebugbar-widgets-params .phpdebugbar-widgets-name {
73 | width: 20%;
74 | font-weight: bold;
75 | }
76 |
77 | div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-error {
78 | display: block;
79 | font-weight: bold;
80 | }
81 |
82 | code.phpdebugbar-widgets-sql {
83 | white-space: pre-wrap;
84 | overflow-wrap: break-word;
85 | word-wrap: break-word;
86 | }
87 |
88 | div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item.phpdebugbar-widgets-sql-duplicate {
89 | background-color: #edeff0;
90 | }
91 |
92 | div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item.phpdebugbar-widgets-sql-duplicate:hover {
93 | background-color: #ffc;
94 | }
95 |
96 | div.phpdebugbar-widgets-sqlqueries div.phpdebugbar-widgets-toolbar {
97 | display:none;
98 | position: fixed;
99 | bottom: 0;
100 | width: 100%;
101 | background: #fff;
102 | z-index: 1;
103 | }
104 |
105 | div.phpdebugbar-widgets-sqlqueries div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter {
106 | float: right;
107 | font-size: 12px;
108 | padding: 2px 4px;
109 | background: #7cacd5;
110 | margin: 0 2px;
111 | border-radius: 4px;
112 | color: #fff;
113 | text-decoration: none;
114 | }
115 | div.phpdebugbar-widgets-sqlqueries div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter.phpdebugbar-widgets-excluded {
116 | background: #eee;
117 | color: #888;
118 | }
119 |
--------------------------------------------------------------------------------
/assets/widgets/sqlqueries/widget.js:
--------------------------------------------------------------------------------
1 | (function($) {
2 |
3 | var csscls = PhpDebugBar.utils.makecsscls('phpdebugbar-widgets-');
4 |
5 | /**
6 | * Widget for the displaying sql queries
7 | *
8 | * Options:
9 | * - data
10 | */
11 | var SQLQueriesWidget = PhpDebugBar.Widgets.SQLQueriesWidget = PhpDebugBar.Widget.extend({
12 |
13 | className: csscls('sqlqueries'),
14 |
15 | onFilterClick: function(el) {
16 | $(el).toggleClass(csscls('excluded'));
17 |
18 | var excludedLabels = [];
19 | this.$toolbar.find(csscls('.filter') + csscls('.excluded')).each(function() {
20 | excludedLabels.push(this.rel);
21 | });
22 |
23 | this.$list.$el.find("li[connection=" + $(el).attr("rel") + "]").toggle();
24 |
25 | this.set('exclude', excludedLabels);
26 | },
27 | onCopyToClipboard: function (el) {
28 | var code = $(el).parent('li').find('code').get(0);
29 | var copy = function () {
30 | try {
31 | document.execCommand('copy');
32 | alert('Query copied to the clipboard');
33 | } catch (err) {
34 | console.log('Oops, unable to copy');
35 | }
36 | };
37 | var select = function (node) {
38 | if (document.selection) {
39 | var range = document.body.createTextRange();
40 | range.moveToElementText(node);
41 | range.select();
42 | } else if (window.getSelection) {
43 | var range = document.createRange();
44 | range.selectNodeContents(node);
45 | window.getSelection().removeAllRanges();
46 | window.getSelection().addRange(range);
47 | }
48 | copy();
49 | window.getSelection().removeAllRanges();
50 | };
51 | select(code);
52 | },
53 | render: function() {
54 | this.$status = $('').addClass(csscls('status')).appendTo(this.$el);
55 |
56 | this.$toolbar = $('').addClass(csscls('toolbar')).appendTo(this.$el);
57 |
58 | var filters = [], self = this;
59 |
60 | this.$list = new PhpDebugBar.Widgets.ListWidget({ itemRenderer: function(li, stmt) {
61 | $('
').addClass(csscls('sql')).html(PhpDebugBar.Widgets.highlight(stmt.sql, 'sql')).appendTo(li);
62 | if (stmt.duration_str) {
63 | $('').addClass(csscls('duration')).text(stmt.duration_str).appendTo(li);
64 | }
65 | if (stmt.memory_str) {
66 | $('').addClass(csscls('memory')).text(stmt.memory_str).appendTo(li);
67 | }
68 | if (typeof(stmt.row_count) != 'undefined') {
69 | $('').addClass(csscls('row-count')).text(stmt.row_count).appendTo(li);
70 | }
71 | if (typeof(stmt.stmt_id) != 'undefined' && stmt.stmt_id) {
72 | $('').addClass(csscls('stmt-id')).text(stmt.stmt_id).appendTo(li);
73 | }
74 | if (stmt.connection) {
75 | $('').addClass(csscls('database')).text(stmt.connection).appendTo(li);
76 | li.attr("connection",stmt.connection);
77 | if ( $.inArray(stmt.connection, filters) == -1 ) {
78 | filters.push(stmt.connection);
79 | $('')
80 | .addClass(csscls('filter'))
81 | .text(stmt.connection)
82 | .attr('rel', stmt.connection)
83 | .on('click', function() { self.onFilterClick(this); })
84 | .appendTo(self.$toolbar);
85 | if (filters.length>1) {
86 | self.$toolbar.show();
87 | self.$list.$el.css("margin-bottom","20px");
88 | }
89 | }
90 | }
91 | if (typeof(stmt.is_success) != 'undefined' && !stmt.is_success) {
92 | li.addClass(csscls('error'));
93 | li.append($('').addClass(csscls('error')).text("[" + stmt.error_code + "] " + stmt.error_message));
94 | }
95 | $('')
96 | .addClass(csscls('copy-clipboard'))
97 | .css('cursor', 'pointer')
98 | .on('click', function (event) {
99 | self.onCopyToClipboard(this);
100 | event.stopPropagation();
101 | })
102 | .appendTo(li);
103 | if (stmt.params && !$.isEmptyObject(stmt.params)) {
104 | var table = $('').addClass(csscls('params')).appendTo(li);
105 | for (var key in stmt.params) {
106 | if (typeof stmt.params[key] !== 'function') {
107 | table.append('' + key + ' | ' + stmt.params[key] + ' |
');
109 | }
110 | }
111 | li.css('cursor', 'pointer').click(function() {
112 | if (table.is(':visible')) {
113 | table.hide();
114 | } else {
115 | table.show();
116 | }
117 | });
118 | }
119 | }});
120 | this.$list.$el.appendTo(this.$el);
121 |
122 | this.bindAttr('data', function(data) {
123 | // the PDO collector maybe is empty
124 | if (data.length <= 0) {
125 | return false;
126 | }
127 | this.$list.set('data', data.statements);
128 | this.$status.empty();
129 |
130 | // Search for duplicate statements.
131 | for (var sql = {}, unique = 0, duplicate = 0, i = 0; i < data.statements.length; i++) {
132 | var stmt = data.statements[i].sql;
133 | if (data.statements[i].params && !$.isEmptyObject(data.statements[i].params)) {
134 | stmt += ' {' + $.param(data.statements[i].params, false) + '}';
135 | }
136 | sql[stmt] = sql[stmt] || { keys: [] };
137 | sql[stmt].keys.push(i);
138 | }
139 | // Add classes to all duplicate SQL statements.
140 | for (var stmt in sql) {
141 | if (sql[stmt].keys.length > 1) {
142 | duplicate += sql[stmt].keys.length;
143 | for (var i = 0; i < sql[stmt].keys.length; i++) {
144 | this.$list.$el.find('.' + csscls('list-item')).eq(sql[stmt].keys[i])
145 | .addClass(csscls('sql-duplicate'));
146 | }
147 | } else {
148 | unique++;
149 | }
150 | }
151 |
152 | var t = $('').text(data.nb_statements + " statements were executed").appendTo(this.$status);
153 | if (data.nb_failed_statements) {
154 | t.append(", " + data.nb_failed_statements + " of which failed");
155 | }
156 | if (duplicate) {
157 | t.append(", " + duplicate + " of which were duplicates");
158 | t.append(", " + unique + " unique");
159 | }
160 | if (data.accumulated_duration_str) {
161 | this.$status.append($('').addClass(csscls('duration')).text(data.accumulated_duration_str));
162 | }
163 | if (data.memory_usage_str) {
164 | this.$status.append($('').addClass(csscls('memory')).text(data.memory_usage_str));
165 | }
166 | });
167 | }
168 |
169 | });
170 |
171 | })(PhpDebugBar.$);
172 |
--------------------------------------------------------------------------------
/assets/widgets/templates/widget.css:
--------------------------------------------------------------------------------
1 |
2 | div.phpdebugbar-widgets-templates div.phpdebugbar-widgets-status {
3 | font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
4 | padding: 6px 6px;
5 | border-bottom: 1px solid #ddd;
6 | font-weight: bold;
7 | color: #555;
8 | background: #fafafa;
9 | }
10 |
11 | div.phpdebugbar-widgets-templates span.phpdebugbar-widgets-render-time,
12 | div.phpdebugbar-widgets-templates span.phpdebugbar-widgets-memory,
13 | div.phpdebugbar-widgets-templates span.phpdebugbar-widgets-param-count,
14 | div.phpdebugbar-widgets-templates span.phpdebugbar-widgets-type {
15 | float: right;
16 | margin-left: 8px;
17 | color: #888;
18 | }
19 | div.phpdebugbar-widgets-templates div.phpdebugbar-widgets-status span.phpdebugbar-widgets-render-time,
20 | div.phpdebugbar-widgets-templates div.phpdebugbar-widgets-status span.phpdebugbar-widgets-memory,
21 | div.phpdebugbar-widgets-templates div.phpdebugbar-widgets-status span.phpdebugbar-widgets-param-count,
22 | div.phpdebugbar-widgets-templates div.phpdebugbar-widgets-status span.phpdebugbar-widgets-type {
23 | color: #555;
24 | }
25 | div.phpdebugbar-widgets-templates span.phpdebugbar-widgets-render-time:before,
26 | div.phpdebugbar-widgets-templates span.phpdebugbar-widgets-memory:before,
27 | div.phpdebugbar-widgets-templates span.phpdebugbar-widgets-param-count:before,
28 | div.phpdebugbar-widgets-templates span.phpdebugbar-widgets-type:before,
29 | div.phpdebugbar-widgets-templates a.phpdebugbar-widgets-editor-link:before
30 | {
31 | font-family: PhpDebugbarFontAwesome;
32 | margin-right: 4px;
33 | font-size: 12px;
34 | }
35 | div.phpdebugbar-widgets-templates span.phpdebugbar-widgets-render-time:before {
36 | content: "\f017";
37 | }
38 | div.phpdebugbar-widgets-templates span.phpdebugbar-widgets-memory:before {
39 | content: "\f085";
40 | }
41 | div.phpdebugbar-widgets-templates span.phpdebugbar-widgets-param-count:before {
42 | content: "\f0ce";
43 | }
44 | div.phpdebugbar-widgets-templates span.phpdebugbar-widgets-type:before {
45 | content: "\f121";
46 | }
47 | div.phpdebugbar-widgets-templates a.phpdebugbar-widgets-editor-link:before {
48 | content: "\f08e";
49 | margin-left: 4px;
50 | }
51 | div.phpdebugbar-widgets-templates table.phpdebugbar-widgets-params {
52 | display: none;
53 | width: 70%;
54 | margin: 10px;
55 | border: 1px solid #ddd;
56 | font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
57 | border-collapse: collapse;
58 | }
59 | div.phpdebugbar-widgets-templates table.phpdebugbar-widgets-params td {
60 | border: 1px solid #ddd;
61 | padding: 0 5px;
62 | }
63 | div.phpdebugbar-widgets-templates table.phpdebugbar-widgets-params .phpdebugbar-widgets-name {
64 | width: 20%;
65 | font-weight: bold;
66 | }
67 |
--------------------------------------------------------------------------------
/assets/widgets/templates/widget.js:
--------------------------------------------------------------------------------
1 | (function($) {
2 |
3 | var csscls = PhpDebugBar.utils.makecsscls('phpdebugbar-widgets-');
4 |
5 | /**
6 | * Widget for the displaying templates data
7 | *
8 | * Options:
9 | * - data
10 | */
11 | var TemplatesWidget = PhpDebugBar.Widgets.TemplatesWidget = PhpDebugBar.Widget.extend({
12 |
13 | className: csscls('templates'),
14 |
15 | render: function() {
16 | this.$status = $('').addClass(csscls('status')).appendTo(this.$el);
17 |
18 | this.$list = new PhpDebugBar.Widgets.ListWidget({ itemRenderer: function(li, tpl) {
19 | $('').addClass(csscls('name')).text(tpl.name).appendTo(li);
20 |
21 | if (typeof tpl.xdebug_link !== 'undefined' && tpl.xdebug_link !== null) {
22 | if (tpl.xdebug_link.ajax) {
23 | $('').on('click', function () {
24 | $.ajax(tpl.xdebug_link.url);
25 | }).addClass(csscls('editor-link')).appendTo(li);
26 | } else {
27 | $('').addClass(csscls('editor-link')).appendTo(li);
28 | }
29 | }
30 | if (tpl.render_time_str) {
31 | $('').addClass(csscls('render-time')).text(tpl.render_time_str).appendTo(li);
32 | }
33 | if (tpl.memory_str) {
34 | $('').addClass(csscls('memory')).text(tpl.memory_str).appendTo(li);
35 | }
36 | if (typeof(tpl.param_count) != 'undefined') {
37 | $('').addClass(csscls('param-count')).text(tpl.param_count).appendTo(li);
38 | }
39 | if (typeof(tpl.type) != 'undefined' && tpl.type) {
40 | $('').addClass(csscls('type')).text(tpl.type).appendTo(li);
41 | }
42 | if (tpl.params && !$.isEmptyObject(tpl.params)) {
43 | var table = $('').addClass(csscls('params')).appendTo(li);
44 | for (var key in tpl.params) {
45 | if (typeof tpl.params[key] !== 'function') {
46 | table.append('' + key + ' | ' + tpl.params[key] + '
|
');
48 | }
49 | }
50 | li.css('cursor', 'pointer').click(function() {
51 | if (table.is(':visible')) {
52 | table.hide();
53 | } else {
54 | table.show();
55 | }
56 | });
57 | }
58 | }});
59 | this.$list.$el.appendTo(this.$el);
60 | this.$callgraph = $('').addClass(csscls('callgraph')).appendTo(this.$el);
61 |
62 | this.bindAttr('data', function(data) {
63 | this.$list.set('data', data.templates);
64 | this.$status.empty();
65 | this.$callgraph.empty();
66 |
67 | var sentence = data.sentence || "templates were rendered";
68 | $('').text(data.nb_templates + " " + sentence).appendTo(this.$status);
69 |
70 | if (data.accumulated_render_time_str) {
71 | this.$status.append($('').addClass(csscls('render-time')).text(data.accumulated_render_time_str));
72 | }
73 | if (data.memory_usage_str) {
74 | this.$status.append($('').addClass(csscls('memory')).text(data.memory_usage_str));
75 | }
76 | if (data.nb_blocks > 0) {
77 | $('').text(data.nb_blocks + " blocks were rendered").appendTo(this.$status);
78 | }
79 | if (data.nb_macros > 0) {
80 | $('').text(data.nb_macros + " macros were rendered").appendTo(this.$status);
81 | }
82 | if (typeof data.callgraph !== 'undefined') {
83 | this.$callgraph.html(data.callgraph);
84 | }
85 | });
86 | }
87 |
88 | });
89 |
90 | })(PhpDebugBar.$);
91 |
--------------------------------------------------------------------------------
/code/Aspects/CacheAfterCallAspect.php:
--------------------------------------------------------------------------------
1 |
25 | ['result' => $result]
26 | ]
27 | );
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/code/Bridge/MonologCollector.php:
--------------------------------------------------------------------------------
1 |
18 | * $debugbar->addCollector(new MonologCollector($logger));
19 | *
20 | */
21 | class MonologCollector extends AbstractProcessingHandler implements DataCollectorInterface, Renderable, MessagesAggregateInterface
22 | {
23 | protected $name;
24 |
25 | protected $records = [];
26 |
27 | /**
28 | * @param Logger $logger
29 | * @param int $level
30 | * @param boolean $bubble
31 | * @param string $name
32 | */
33 | public function __construct(?Logger $logger = null, $level = Logger::DEBUG, $bubble = true, $name = 'monolog')
34 | {
35 | parent::__construct($level, $bubble);
36 | $this->name = $name;
37 | if ($logger !== null) {
38 | $this->addLogger($logger);
39 | }
40 | }
41 |
42 | /**
43 | * Adds logger which messages you want to log
44 | *
45 | * @param Logger $logger
46 | */
47 | public function addLogger(Logger $logger)
48 | {
49 | $logger->pushHandler($this);
50 | }
51 |
52 | protected function write(LogRecord $record): void
53 | {
54 | $this->records[] = [
55 | 'message' => $record['formatted'],
56 | 'is_string' => true,
57 | 'label' => strtolower($record['level_name']),
58 | 'time' => $record['datetime']->format('U')
59 | ];
60 | }
61 |
62 | /**
63 | * @return array
64 | */
65 | public function getMessages()
66 | {
67 | return $this->records;
68 | }
69 |
70 | /**
71 | * @return array
72 | */
73 | public function collect()
74 | {
75 | return [
76 | 'count' => count($this->records),
77 | 'records' => $this->records
78 | ];
79 | }
80 |
81 | /**
82 | * @return string
83 | */
84 | public function getName()
85 | {
86 | return $this->name;
87 | }
88 |
89 | /**
90 | * @return array
91 | */
92 | public function getWidgets()
93 | {
94 | $name = $this->getName();
95 | return [
96 | $name => [
97 | "icon" => "suitcase",
98 | "widget" => "PhpDebugBar.Widgets.MessagesWidget",
99 | "map" => "$name.records",
100 | "default" => "[]"
101 | ],
102 | "$name:badge" => [
103 | "map" => "$name.count",
104 | "default" => "null"
105 | ]
106 | ];
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/code/Bridge/SymfonyMailer/SymfonyMailerCollector.php:
--------------------------------------------------------------------------------
1 | emails as $msg) {
26 | $mails[] = [
27 | 'to' => $this->formatTo($msg->getTo()),
28 | 'subject' => $msg->getSubject(),
29 | 'headers' => $msg->getHeaders()->toString()
30 | ];
31 | }
32 |
33 | return [
34 | 'count' => count($mails),
35 | 'mails' => $mails
36 | ];
37 | }
38 |
39 | public function add(Email $email)
40 | {
41 | $this->emails[] = $email;
42 | }
43 |
44 | /**
45 | * @param Address[] $to
46 | * @return string
47 | */
48 | protected function formatTo($to)
49 | {
50 | if (empty($to)) {
51 | return '';
52 | }
53 |
54 | $f = [];
55 | foreach ($to as $k => $v) {
56 | $f[] = $v->toString();
57 | }
58 | return implode(', ', $f);
59 | }
60 |
61 | public function getName()
62 | {
63 | return 'symfonymailer_mails';
64 | }
65 |
66 | public function getWidgets()
67 | {
68 | return [
69 | 'emails' => [
70 | 'icon' => 'inbox',
71 | 'widget' => 'PhpDebugBar.Widgets.MailsWidget',
72 | 'map' => 'symfonymailer_mails.mails',
73 | 'default' => '[]',
74 | 'title' => 'Mails'
75 | ],
76 | 'emails:badge' => [
77 | 'map' => 'symfonymailer_mails.count',
78 | 'default' => 'null'
79 | ]
80 | ];
81 | }
82 |
83 | public function getAssets()
84 | {
85 | return [
86 | 'css' => 'widgets/mails/widget.css',
87 | 'js' => 'widgets/mails/widget.js'
88 | ];
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/code/Collector/CacheCollector.php:
--------------------------------------------------------------------------------
1 | '/' . DebugBar::moduleResource('javascript')->getRelativePath(),
29 | 'base_url' => Director::makeRelative(DebugBar::moduleResource('javascript')->getURL()),
30 | 'css' => 'config/widget.css',
31 | 'js' => 'config/widget.js'
32 | ];
33 | }
34 |
35 | public function collect()
36 | {
37 | $result = CacheProxy::getData();
38 |
39 | $keys = [];
40 | $showGet = $this->showGet || isset($_REQUEST['debug_cacheshowget']);
41 | foreach ($result as $k => $v) {
42 | $type = $v['type'] ?? null;
43 | if (!$showGet && $type == "get") {
44 | continue;
45 | }
46 | $val = $v['value'];
47 | if (!is_string($val)) {
48 | $val = json_encode($val);
49 | }
50 | // Long values are trimmed by DebugBar js
51 | if (!empty($v['ttl'])) {
52 | $val .= " - TTL: " . $v['ttl'];
53 | }
54 | if (!empty($v['caller'])) {
55 | $val .= " - (" . $v['caller'] . ")";
56 | }
57 | if ($type == 'set' && $showGet) {
58 | $val = "SET - " . $val;
59 | }
60 | $keys[$v['key']] = $val;
61 | }
62 |
63 | return [
64 | 'count' => count($keys),
65 | 'keys' => $keys
66 | ];
67 | }
68 |
69 | public function getWidgets()
70 | {
71 | $widgets = [
72 | 'Cache' => [
73 | 'icon' => 'puzzle-piece',
74 | 'widget' => 'PhpDebugBar.Widgets.VariableListWidget',
75 | 'map' => 'cache.keys',
76 | 'default' => '{}'
77 | ]
78 | ];
79 | $widgets['Cache:badge'] = [
80 | 'map' => 'cache.count',
81 | 'default' => 0
82 | ];
83 |
84 | return $widgets;
85 | }
86 |
87 | /**
88 | * Get the value of showGet
89 | */
90 | public function getShowGet()
91 | {
92 | return $this->showGet;
93 | }
94 |
95 | /**
96 | * Set the value of showGet
97 | *
98 | * @param bool $showGet
99 | */
100 | public function setShowGet($showGet)
101 | {
102 | $this->showGet = $showGet;
103 | return $this;
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/code/Collector/ConfigCollector.php:
--------------------------------------------------------------------------------
1 | get(Kernel::class)->getConfigLoader();
31 | $manifest = $configLoader->getManifest();
32 | return $manifest;
33 | }
34 |
35 | public function collect()
36 | {
37 | $manifest = $this->getConfigManifest();
38 | $result = [];
39 | if (method_exists($manifest, 'getConfigCalls')) {
40 | $result = $manifest->getConfigCalls();
41 | }
42 | return [
43 | 'count' => count($result),
44 | 'calls' => $result
45 | ];
46 | }
47 |
48 | public function getWidgets()
49 | {
50 | $widgets = [
51 | 'config' => [
52 | 'icon' => 'gear',
53 | 'widget' => 'PhpDebugBar.Widgets.ConfigWidget',
54 | 'map' => 'config.calls',
55 | 'default' => '{}'
56 | ]
57 | ];
58 |
59 | $widgets['config:badge'] = [
60 | 'map' => 'config.count',
61 | 'default' => 0
62 | ];
63 |
64 | return $widgets;
65 | }
66 |
67 | public function getAssets()
68 | {
69 | $name = $this->getName();
70 |
71 | return [
72 | 'base_path' => '/' . DebugBar::moduleResource('javascript')->getRelativePath(),
73 | 'base_url' => Director::makeRelative(DebugBar::moduleResource('javascript')->getURL()),
74 | 'css' => $name . '/widget.css',
75 | 'js' => $name . '/widget.js'
76 | ];
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/code/Collector/DatabaseCollector.php:
--------------------------------------------------------------------------------
1 | timeCollector = DebugBar::getDebugBar()->getCollector('time');
28 |
29 | $data = $this->collectData($this->timeCollector);
30 |
31 | // Check for excessive number of queries
32 | $dbQueryWarningLevel = DebugBar::config()->get('warn_query_limit');
33 | if ($dbQueryWarningLevel && $data['nb_statements'] > $dbQueryWarningLevel) {
34 | $helpLink = DebugBar::config()->get('performance_guide_link');
35 | $messages = DebugBar::getMessageCollector();
36 | if ($messages) {
37 | $messages->addMessage(
38 | "This page ran more than $dbQueryWarningLevel database queries." .
39 | "\nYou could reduce this by implementing caching." .
40 | "\nYou can find more info here: $helpLink",
41 | 'warning',
42 | true
43 | );
44 | }
45 | }
46 |
47 | if (isset($data['show_db']) && $data['show_db']) {
48 | $showDbWarning = DebugBar::config()->get('show_db_warning');
49 | $messages = DebugBar::getMessageCollector();
50 | if ($messages && $showDbWarning) {
51 | $messages->addMessage(
52 | "Consider enabling `optimistic_connect` to avoid an extra query. You can turn this warning off by setting show_db_warning = false",
53 | 'warning',
54 | true
55 | );
56 | }
57 | }
58 |
59 | if (isset($data['has_unclosed_transaction']) && $data['has_unclosed_transaction']) {
60 | $messages = DebugBar::getMessageCollector();
61 | if ($messages) {
62 | $messages->addMessage(
63 | "There is an unclosed transaction on your page and some statement may not be persisted to the database",
64 | 'warning',
65 | true
66 | );
67 | }
68 | }
69 |
70 | return $data;
71 | }
72 |
73 | /**
74 | * Explode comma separated elements not within parenthesis or quotes
75 | *
76 | * @param string $str
77 | * @return array
78 | */
79 | protected static function explodeFields($str)
80 | {
81 | return preg_split("/(?![^(]*\)),/", $str);
82 | }
83 |
84 | /**
85 | * Collects data
86 | *
87 | * @param TimeDataCollector $timeCollector
88 | * @return array
89 | */
90 | protected function collectData(?TimeDataCollector $timeCollector = null)
91 | {
92 | $stmts = [];
93 |
94 | $total_duration = 0;
95 | $total_mem = 0;
96 |
97 | $failed = 0;
98 |
99 | $i = 0;
100 |
101 | // Get queries gathered by proxy
102 | $queries = ProxyDBExtension::getQueries();
103 |
104 | $limit = DebugBar::config()->get('query_limit');
105 | $warnDurationThreshold = DebugBar::config()->get('warn_dbqueries_threshold_seconds');
106 |
107 | // only show db if there is more than one database in use
108 | $showDb = count(array_filter(array_unique(array_map(function ($stmt) {
109 | return $stmt['database'];
110 | }, $queries)))) > 1;
111 |
112 | $showDb = false;
113 | $hasUnclosedTransaction = false;
114 | foreach ($queries as $stmt) {
115 | $i++;
116 |
117 | $total_duration += $stmt['duration'];
118 | $total_mem += $stmt['memory'];
119 |
120 | if (!$stmt['success']) {
121 | $failed++;
122 | }
123 |
124 | if (str_starts_with((string) $stmt['short_query'], 'SHOW DATABASES LIKE')) {
125 | $showDb = true;
126 | }
127 |
128 | if (str_contains((string) $stmt['short_query'], 'START TRANSACTION')) {
129 | $hasUnclosedTransaction = true;
130 | }
131 | if (str_contains((string) $stmt['short_query'], 'END TRANSACTION')) {
132 | $hasUnclosedTransaction = false;
133 | }
134 |
135 | if ($limit && $i > $limit) {
136 | $stmts[] = [
137 | 'sql' => "Only the first $limit queries are shown"
138 | ];
139 | break;
140 | }
141 |
142 | $stmts[] = [
143 | 'sql' => $stmt['short_query'],
144 | 'row_count' => $stmt['rows'],
145 | 'params' => $stmt['select'] ? $stmt['select'] : null,
146 | 'duration' => $stmt['duration'],
147 | 'duration_str' => $this->getDataFormatter()->formatDuration($stmt['duration']),
148 | 'memory' => $stmt['memory'],
149 | 'memory_str' => $this->getDataFormatter()->formatBytes($stmt['memory']),
150 | 'is_success' => $stmt['success'],
151 | 'database' => $showDb ? $stmt['database'] : null,
152 | 'source' => $stmt['source'],
153 | 'warn' => $stmt['duration'] > $warnDurationThreshold
154 | ];
155 |
156 | if ($timeCollector !== null) {
157 | $timeCollector->addMeasure(
158 | $stmt['short_query'],
159 | $stmt['start_time'],
160 | $stmt['end_time']
161 | );
162 | }
163 | }
164 |
165 | // Save as CSV
166 | $db_save_csv = DebugBar::config()->get('db_save_csv');
167 | if ($db_save_csv && !empty($queries)) {
168 | $filename = date('Ymd_His') . '_' . count($queries) . '_' . uniqid() . '.csv';
169 | $isOutput = false;
170 | if (isset($_REQUEST['downloadqueries']) && Director::isDev()) {
171 | $isOutput = true;
172 | if (headers_sent()) {
173 | die('Cannot download queries, headers are already sent');
174 | }
175 | header('Content-Type: text/csv');
176 | header('Content-Disposition: attachment;filename=' . $filename);
177 | $fp = fopen('php://output', 'w');
178 | } else {
179 | $tempFolder = TEMP_FOLDER . '/debugbar/db';
180 | if (!is_dir($tempFolder)) {
181 | mkdir($tempFolder, 0755, true);
182 | }
183 | $fp = fopen($tempFolder . '/' . $filename, 'w');
184 | }
185 | $headers = array_keys($queries[0]);
186 | fputcsv($fp, $headers);
187 | foreach ($queries as $query) {
188 | fputcsv($fp, $query);
189 | }
190 | fclose($fp);
191 |
192 | if ($isOutput) {
193 | die();
194 | }
195 | }
196 |
197 | return [
198 | 'nb_statements' => count($queries),
199 | 'nb_failed_statements' => $failed,
200 | 'show_db' => $showDb,
201 | 'has_unclosed_transaction' => $hasUnclosedTransaction,
202 | 'statements' => $stmts,
203 | 'accumulated_duration' => $total_duration,
204 | 'accumulated_duration_str' => $this->getDataFormatter()->formatDuration($total_duration),
205 | 'memory_usage' => $total_mem,
206 | 'memory_usage_str' => $this->getDataFormatter()->formatBytes($total_mem),
207 | ];
208 | }
209 |
210 | /**
211 | * @return string
212 | */
213 | public function getName()
214 | {
215 | return 'db';
216 | }
217 |
218 | /**
219 | * @return array
220 | */
221 | public function getWidgets()
222 | {
223 | return [
224 | "database" => [
225 | "icon" => "database",
226 | "widget" => "PhpDebugBar.Widgets.SQLQueriesWidget",
227 | "map" => "db",
228 | "default" => "[]"
229 | ],
230 | "database:badge" => [
231 | "map" => "db.nb_statements",
232 | "default" => 0
233 | ]
234 | ];
235 | }
236 |
237 | /**
238 | * @return array
239 | */
240 | public function getAssets()
241 | {
242 | return [
243 | 'base_path' => '/' . DebugBar::moduleResource('javascript')->getRelativePath(),
244 | 'base_url' => Director::makeRelative(DebugBar::moduleResource('javascript')->getURL()),
245 | 'css' => 'sqlqueries/widget.css',
246 | 'js' => 'sqlqueries/widget.js'
247 | ];
248 | }
249 | }
250 |
--------------------------------------------------------------------------------
/code/Collector/HeaderCollector.php:
--------------------------------------------------------------------------------
1 | controller = $controller;
24 | }
25 |
26 | /**
27 | * @return array
28 | */
29 | public function getWidgets()
30 | {
31 | return [
32 | 'Headers' => [
33 | 'icon' => 'gear',
34 | 'widget' => 'PhpDebugBar.Widgets.VariableListWidget',
35 | 'map' => 'Headers.list',
36 | 'default' => '{}'
37 | ],
38 | 'Headers:badge' => [
39 | 'map' => 'Headers.count',
40 | 'default' => 0
41 | ]
42 | ];
43 | }
44 |
45 | /**
46 | * @return array
47 | */
48 | public function collect()
49 | {
50 | $result = $this->controller->getResponse()->getHeaders();
51 |
52 | foreach ($result as $key => &$value) {
53 | $value = trim(implode(PHP_EOL, explode('; ', (string) $value)));
54 | $value = implode(PHP_EOL . ' - ', explode(' ', $value));
55 | }
56 |
57 | return [
58 | 'count' => count($result),
59 | 'list' => $result
60 | ];
61 | }
62 |
63 | /**
64 | * @return string
65 | */
66 | public function getName()
67 | {
68 | return 'Headers';
69 | }
70 |
71 | public function getAssets()
72 | {
73 | return [];
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/code/Collector/PartialCacheCollector.php:
--------------------------------------------------------------------------------
1 | '/' . DebugBar::moduleResource('javascript')->getRelativePath(),
32 | 'base_url' => Director::makeRelative(DebugBar::moduleResource('javascript')->getURL()),
33 | 'css' => 'config/widget.css',
34 | 'js' => 'config/widget.js'
35 | ];
36 | }
37 |
38 | public function collect()
39 | {
40 | $result = self::getTemplateCache();
41 | return [
42 | 'count' => count($result),
43 | 'calls' => $result
44 | ];
45 | }
46 |
47 | public function getWidgets()
48 | {
49 | $widgets = [
50 | 'Partial Cache' => [
51 | 'icon' => 'cube',
52 | 'widget' => 'PhpDebugBar.Widgets.ConfigWidget',
53 | 'map' => 'partial-cache.calls',
54 | 'default' => '{}'
55 | ]
56 | ];
57 | if (count(self::getTemplateCache()) > 0) {
58 | $widgets['Partial Cache:badge'] = [
59 | 'map' => 'partial-cache.count',
60 | 'default' => 0
61 | ];
62 | }
63 |
64 | return $widgets;
65 | }
66 |
67 | /**
68 | * @return array
69 | */
70 | public static function getTemplateCache()
71 | {
72 | return (self::$templateCache) ?: [];
73 | }
74 |
75 | /**
76 | * Adds an item to the templateCache array
77 | * @param string $key
78 | * @param array $item
79 | */
80 | public static function addTemplateCache($key, $item)
81 | {
82 | self::$templateCache[$key] = $item;
83 | }
84 |
85 | /**
86 | * @param array $templateCache
87 | */
88 | public static function setTemplateCache($templateCache)
89 | {
90 | self::$templateCache = $templateCache;
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/code/Collector/PhpInfoCollector.php:
--------------------------------------------------------------------------------
1 | trimVersion($metrics['version']);
14 | return $metrics;
15 | }
16 |
17 | /**
18 | * Given a PHP_VERSION constant value, trim any "extra" meta information from the end, returning the major, minor
19 | * and patch release of the version. Will fall back to returning the input if the matching fails.
20 | *
21 | * @param string $version
22 | * @return string
23 | */
24 | protected function trimVersion($version)
25 | {
26 | preg_match('/^\d+\.\d+\.\d+/', $version, $matches);
27 | return !empty($matches[0]) ? $matches[0] : $version;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/code/Collector/SilverStripeCollector.php:
--------------------------------------------------------------------------------
1 | count(self::$debug),
28 | 'debug' => self::$debug,
29 | 'session' => self::getSessionData(),
30 | 'config' => self::getConfigData(),
31 | 'locale' => i18n::get_locale(),
32 | 'version' => class_exists(LeftAndMain::class) ? LeftAndMain::create()->CMSVersion() : 'unknown',
33 | 'cookies' => self::getCookieData(),
34 | 'parameters' => self::getRequestParameters(),
35 | 'requirements' => self::getRequirementsData(),
36 | 'user' => Security::getCurrentUser() ? Security::getCurrentUser()->Title : 'Not logged in',
37 | 'templates' => self::getTemplateData(),
38 | 'middlewares' => self::getMiddlewares(),
39 | ];
40 | return $data;
41 | }
42 |
43 | /**
44 | * Get all middlewares executed on this request
45 | *
46 | * @return array
47 | */
48 | public static function getMiddlewares()
49 | {
50 | $middlewares = Director::singleton()->getMiddlewares();
51 | if (!$middlewares) {
52 | return [
53 | 'list' => [],
54 | 'count' => 0,
55 | ];
56 | }
57 | return [
58 | 'list' => array_keys($middlewares),
59 | 'count' => count($middlewares)
60 | ];
61 | }
62 |
63 | /**
64 | * Returns the names of all the templates rendered.
65 | *
66 | * @return array
67 | */
68 | public static function getTemplateData()
69 | {
70 | $templates = SSViewerProxy::getTemplatesUsed();
71 | return [
72 | 'templates' => $templates,
73 | 'count' => count($templates)
74 | ];
75 | }
76 |
77 | public static function getRequirementsData()
78 | {
79 | $backend = Requirements::backend();
80 |
81 | $requirements = array_merge(
82 | $backend->getCSS(),
83 | $backend->getJavascript()
84 | );
85 |
86 | $output = [];
87 | foreach ($requirements as $asset => $specs) {
88 | // Get the filename only
89 | $fileNames = explode('/', $asset);
90 | $fileName = end($fileNames);
91 | $specs['href'] = $asset;
92 | $output[$fileName] = json_encode($specs, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
93 | }
94 |
95 | return [
96 | 'list' => $output,
97 | 'count' => count($requirements)
98 | ];
99 | }
100 |
101 | public static function getRequestParameters()
102 | {
103 | if (!self::$controller) {
104 | return [];
105 | }
106 | $request = self::$controller->getRequest();
107 |
108 | $p = [];
109 | foreach ($request->getVars() as $k => $v) {
110 | $p["GET - $k"] = $v;
111 | }
112 | foreach ($request->postVars() as $k => $v) {
113 | $p["POST - $k"] = $v;
114 | }
115 | foreach ($request->params() as $k => $v) {
116 | $p["ROUTE - $k"] = $v;
117 | }
118 | return $p;
119 | }
120 |
121 | public static function getCookieData()
122 | {
123 | return Cookie::get_all();
124 | }
125 |
126 | public static function getSessionData()
127 | {
128 | $data = DebugBar::getRequest()->getSession()->getAll();
129 |
130 | if (empty($data)) {
131 | return [];
132 | }
133 |
134 | $filtered = [];
135 |
136 | // Filter not useful data
137 | foreach ($data as $k => $v) {
138 | if (strpos($k, 'gf_') === 0) {
139 | continue;
140 | }
141 | if ($k === 'PHPDEBUGBAR_STACK_DATA') {
142 | continue;
143 | }
144 | if (is_array($v)) {
145 | $v = json_encode($v, JSON_PRETTY_PRINT);
146 | }
147 | $filtered[$k] = $v;
148 | }
149 | return $filtered;
150 | }
151 |
152 | public static function getConfigData()
153 | {
154 | if (!class_exists(SiteConfig::class)) {
155 | return [];
156 | }
157 | return SiteConfig::current_site_config()->toMap();
158 | }
159 |
160 | /**
161 | * This method will try to matches all messages into a proper array
162 | *
163 | * @param string $data
164 | */
165 | public static function setDebugData($data)
166 | {
167 | $matches = null;
168 |
169 | preg_match_all("/\n(.*?)<\/p>/s", $data, $matches);
170 |
171 | if (!empty($matches[1])) {
172 | self::$debug = $matches[1];
173 | }
174 | }
175 |
176 | /**
177 | * @param Controller $controller
178 | * @return $this
179 | */
180 | public function setController($controller)
181 | {
182 | self::$controller = $controller;
183 | return $this;
184 | }
185 |
186 | /**
187 | * @return Controller
188 | */
189 | public function getController()
190 | {
191 | return self::$controller;
192 | }
193 |
194 | public function getName()
195 | {
196 | return 'silverstripe';
197 | }
198 |
199 | public function getWidgets()
200 | {
201 | $name = $this->getName();
202 |
203 | $userIcon = 'user-times';
204 | $userText = 'Not logged in';
205 | if ($member = Security::getCurrentUser()) {
206 | $memberTag = $member->getTitle() . ' (#' . $member->ID . ')';
207 |
208 | $userIcon = 'user';
209 | $userText = 'Logged in as ' . $memberTag;
210 |
211 | // TODO: upgrade to newer version of the module
212 | // Masquerade integration
213 | if (DebugBar::getRequest()->getSession()->get('Masquerade.Old.loggedInAs')) {
214 | $userIcon = 'user-secret';
215 | $userText = 'Masquerading as member ' . $memberTag;
216 | }
217 | }
218 |
219 | $widgets = [
220 | 'user' => [
221 | 'icon' => $userIcon,
222 | 'tooltip' => $userText,
223 | "default" => "",
224 | ],
225 | "version" => [
226 | "icon" => "hashtag",
227 | "tooltip" => class_exists(LeftAndMain::class) ? LeftAndMain::create()->CMSVersion() : 'unknown',
228 | "default" => ""
229 | ],
230 | "locale" => [
231 | "icon" => "globe",
232 | "tooltip" => i18n::get_locale(),
233 | "default" => "",
234 | ],
235 | "session" => [
236 | "icon" => "archive",
237 | "widget" => "PhpDebugBar.Widgets.VariableListWidget",
238 | "map" => "$name.session",
239 | "default" => "{}"
240 | ],
241 | "cookies" => [
242 | "icon" => "asterisk",
243 | "widget" => "PhpDebugBar.Widgets.VariableListWidget",
244 | "map" => "$name.cookies",
245 | "default" => "{}"
246 | ],
247 | "parameters" => [
248 | "icon" => "arrow-right",
249 | "widget" => "PhpDebugBar.Widgets.VariableListWidget",
250 | "map" => "$name.parameters",
251 | "default" => "{}"
252 | ],
253 | "requirements" => [
254 | "icon" => "file-text-o",
255 | "widget" => "PhpDebugBar.Widgets.VariableListWidget",
256 | "map" => "$name.requirements.list",
257 | "default" => "{}"
258 | ],
259 | "requirements:badge" => [
260 | "map" => "$name.requirements.count",
261 | "default" => 0
262 | ],
263 | "middlewares" => [
264 | "icon" => "file-text-o",
265 | "widget" => "PhpDebugBar.Widgets.ListWidget",
266 | "map" => "$name.middlewares.list",
267 | "default" => "{}"
268 | ],
269 | "middlewares:badge" => [
270 | "map" => "$name.middlewares.count",
271 | "default" => 0
272 | ],
273 | 'templates' => [
274 | 'icon' => 'file-code-o',
275 | 'widget' => 'PhpDebugBar.Widgets.ListWidget',
276 | 'map' => "$name.templates.templates",
277 | 'default' => '{}'
278 | ],
279 | 'templates:badge' => [
280 | 'map' => "$name.templates.count",
281 | 'default' => 0
282 | ]
283 | ];
284 |
285 | if (!empty(static::getConfigData())) {
286 | $widgets["SiteConfig"] = [
287 | "icon" => "sliders",
288 | "widget" => "PhpDebugBar.Widgets.VariableListWidget",
289 | "map" => "$name.config",
290 | "default" => "{}"
291 | ];
292 | }
293 |
294 | if (!empty(self::$debug)) {
295 | $widgets["debug"] = [
296 | "icon" => "list-alt",
297 | "widget" => "PhpDebugBar.Widgets.ListWidget",
298 | "map" => "$name.debug",
299 | "default" => "[]"
300 | ];
301 | $widgets["debug:badge"] = [
302 | "map" => "$name.debugcount",
303 | "default" => "null"
304 | ];
305 | }
306 |
307 | return $widgets;
308 | }
309 |
310 | /**
311 | * @return array
312 | */
313 | public function getAssets()
314 | {
315 | return [
316 | 'base_path' => '/' . DebugBar::moduleResource('javascript')->getRelativePath(),
317 | 'base_url' => Director::makeRelative(DebugBar::moduleResource('javascript')->getURL()),
318 | 'css' => [],
319 | 'js' => 'widgets.js',
320 | ];
321 | }
322 | }
323 |
--------------------------------------------------------------------------------
/code/Collector/TimeDataCollector.php:
--------------------------------------------------------------------------------
1 | get('warn_request_time_seconds');
20 | $warningRatio = DebugBar::config()->get('warn_warning_ratio');
21 |
22 | // Can be disabled by setting the value to false
23 | if (!is_numeric($upperThreshold)) {
24 | return $widgets;
25 | }
26 |
27 | $widgets['time']['indicator'] = 'PhpDebugBar.DebugBar.WarnableRequestTimeIndicator';
28 | $widgets['time']['warn'] = 'ok';
29 | // Request duration rather than Request Duration
30 | $widgets['time']['tooltip'] = ucfirst(strtolower((string) $widgets['time']['tooltip']));
31 |
32 | $warningThreshold = $upperThreshold * $warningRatio;
33 | if ($this->getRequestDuration() > $upperThreshold) {
34 | $widgets['time']['warn'] = 'danger';
35 | $widgets['time']['tooltip'] .= ' > ' . $upperThreshold . ' seconds';
36 | } elseif ($this->getRequestDuration() > $warningThreshold) {
37 | $widgets['time']['warn'] = 'warning';
38 | $widgets['time']['tooltip'] .= ' > ' . $warningThreshold . ' seconds';
39 | } else {
40 | $widgets['time']['tooltip'] .= ' < ' . $warningThreshold . ' seconds';
41 | }
42 |
43 | return $widgets;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/code/DebugBarController.php:
--------------------------------------------------------------------------------
1 | get('enable_storage')) {
20 | return $this->httpError(404, 'Storage not enabled');
21 | }
22 | $debugbar = DebugBar::getDebugBar();
23 | if (!$debugbar) {
24 | return $this->httpError(404, 'DebugBar not enabled');
25 | }
26 | $openHandler = new OpenHandler($debugbar);
27 | $openHandler->handle();
28 | exit(); // Handle will echo and set headers
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/code/DebugBarUtils.php:
--------------------------------------------------------------------------------
1 | format($query);
20 | }
21 | if (class_exists(\SqlFormatter::class)) {
22 | return \SqlFormatter::format($query, !self::isCli());
23 | }
24 | if (class_exists(\SilverStripe\View\Parsers\SQLFormatter::class)) {
25 | $method = self::isCli() ? 'formatPlain' : 'formatHTML';
26 | $formatter = new \SilverStripe\View\Parsers\SQLFormatter;
27 | return $formatter->$method($query);
28 | }
29 | return $query;
30 | }
31 |
32 | /**
33 | * Show the underlying sql query for a list
34 | * @param DataList $list
35 | * @param bool $inline Inlines paramters
36 | * @param bool $noQuotes Remove ANSI Quotes
37 | * @return string
38 | */
39 | public static function formatListQuery(DataList $list, bool $inline = true, bool $noQuotes = false): string
40 | {
41 | $parameters = [];
42 | $formatted = self::formatSql($list->sql($parameters));
43 | if ($inline) {
44 | $formatted = DB::inline_parameters($formatted, $parameters);
45 | }
46 | if ($noQuotes) {
47 | $formatted = str_replace('"', '', $formatted);
48 | }
49 | return $formatted;
50 | }
51 |
52 | public static function isCli(): bool
53 | {
54 | return in_array(PHP_SAPI ?: '', ['cli', 'phpdbg']) || !http_response_code();
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/code/Extension/ControllerExtension.php:
--------------------------------------------------------------------------------
1 | getCollector('silverstripe');
23 | $ssCollector->setController(Controller::curr());
24 |
25 | /** @var DebugBar\DataCollector\TimeDataCollector $timeData */
26 | $timeData = $debugbar->getCollector('time');
27 | if (!$timeData) {
28 | return;
29 | }
30 | if ($timeData->hasStartedMeasure('pre_request')) {
31 | $timeData->stopMeasure("pre_request");
32 | }
33 | $timeData->startMeasure("init", get_class($this->owner) . ' init');
34 | });
35 | }
36 |
37 | public function onAfterInit()
38 | {
39 | // Avoid requirements being called to early due to RootURLController/ModelAsController
40 | if ($this->owner instanceof \SilverStripe\CMS\Controllers\RootURLController) {
41 | return;
42 | }
43 | if ($this->owner instanceof \SilverStripe\CMS\Controllers\ModelAsController) {
44 | return;
45 | }
46 | DebugBar::includeRequirements();
47 |
48 | DebugBar::withDebugBar(function (\DebugBar\DebugBar $debugbar) {
49 | // Add the headers Collector
50 | if (!$debugbar->hasCollector('Headers') && DebugBar::config()->get('header_collector')) {
51 | $debugbar->addCollector(new HeaderCollector($this->owner));
52 | }
53 | /** @var DebugBar\DataCollector\TimeDataCollector $timeData */
54 | $timeData = $debugbar->getCollector('time');
55 | if (!$timeData) {
56 | return;
57 | }
58 | if ($timeData->hasStartedMeasure("cms_init")) {
59 | $timeData->stopMeasure("cms_init");
60 | }
61 | if ($timeData->hasStartedMeasure("init")) {
62 | $timeData->stopMeasure("init");
63 | }
64 | $timeData->startMeasure("handle", get_class($this->owner) . ' handle request');
65 | });
66 | }
67 |
68 | /**
69 | * @param HTTPRequest $request
70 | * @param string $action
71 | */
72 | public function beforeCallActionHandler($request, $action)
73 | {
74 | // If we don't have an action, getViewer will be called immediatly
75 | // If we have custom routes, request action is different than action
76 | $allParams = $request->allParams();
77 | $requestAction = null;
78 | if (!empty($allParams['Action'])) {
79 | $requestAction = $allParams['Action'];
80 | }
81 | if (!$this->owner->hasMethod($action) || ($requestAction && $requestAction != $action)) {
82 | self::clearBuffer();
83 | }
84 |
85 | DebugBar::withDebugBar(function (\DebugBar\DebugBar $debugBar) use ($action) {
86 | /** @var DebugBar\DataCollector\TimeDataCollector $timeData */
87 | $timeData = $debugBar->getCollector('time');
88 | if (!$timeData) {
89 | return;
90 | }
91 | if ($timeData->hasStartedMeasure("handle")) {
92 | $timeData->stopMeasure("handle");
93 | }
94 | $timeData->startMeasure("action", get_class($this->owner) . " action $action");
95 | });
96 | }
97 |
98 | /**
99 | * @param HTTPRequest $request
100 | * @param string $action
101 | * @param mixed $result
102 | */
103 | public function afterCallActionHandler($request, $action, $result)
104 | {
105 | self::clearBuffer();
106 |
107 | DebugBar::withDebugBar(function (\DebugBar\DebugBar $debugBar) use ($action) {
108 | /** @var DebugBar\DataCollector\TimeDataCollector $timeData */
109 | $timeData = $debugBar->getCollector('time');
110 | if (!$timeData) {
111 | return;
112 | }
113 | if ($timeData->hasStartedMeasure("action")) {
114 | $timeData->stopMeasure("action");
115 | }
116 | $timeData->startMeasure(
117 | "after_action",
118 | get_class($this->owner) . " after action $action"
119 | );
120 | });
121 | }
122 |
123 | protected static function clearBuffer()
124 | {
125 | if (!DebugBar::$bufferingEnabled) {
126 | return;
127 | }
128 | $buffer = ob_get_clean();
129 | if (!empty($buffer)) {
130 | unset($_REQUEST['debug_request']); // Disable further messages that we can't intercept
131 | SilverStripeCollector::setDebugData($buffer);
132 | }
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/code/Extension/DebugMailerExtension.php:
--------------------------------------------------------------------------------
1 | getCollector('symfonymailer_mails');
31 | $mailerCollector->add($email);
32 | });
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/code/Extension/LeftAndMainExtension.php:
--------------------------------------------------------------------------------
1 | getCollector('time');
18 | if (!$timeData) {
19 | return;
20 | }
21 | if ($timeData->hasStartedMeasure("init")) {
22 | $timeData->stopMeasure("init");
23 | }
24 | $timeData->startMeasure("cms_accessed", "cms accessed");
25 | });
26 | }
27 |
28 | public function init()
29 | {
30 | DebugBar::withDebugBar(function (\DebugBar\DebugBar $debugbar) {
31 | /** @var DebugBar\DataCollector\TimeDataCollector $timeData */
32 | $timeData = $debugbar->getCollector('time');
33 | if (!$timeData) {
34 | return;
35 | }
36 | if ($timeData->hasStartedMeasure("cms_accessed")) {
37 | $timeData->stopMeasure("cms_accessed");
38 | }
39 | $timeData->startMeasure("cms_init", "cms init");
40 | });
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/code/Extension/ProxyDBExtension.php:
--------------------------------------------------------------------------------
1 | >
20 | */
21 | protected static $queries = [];
22 |
23 | /**
24 | * Find source toggle (set by config find_source)
25 | *
26 | * @var ?bool
27 | */
28 | protected static $findSource = null;
29 |
30 | /**
31 | * @param ProxyGenerator $proxy
32 | * @return void
33 | */
34 | public function updateProxy(ProxyGenerator &$proxy)
35 | {
36 | if (DebugBar::getDebugBar() === false) {
37 | return;
38 | }
39 | if (self::$findSource === null) {
40 | self::$findSource = DebugBar::config()->get('find_source');
41 | }
42 |
43 | // In the closure, $this is the proxied database
44 | $callback = function ($args, $next) {
45 |
46 | // The first argument is always the sql query
47 | $sql = $args[0];
48 | $parameters = isset($args[2]) ? $args[2] : [];
49 |
50 | // Sql can be an array
51 | if (is_array($sql)) {
52 | $parameters = $sql[1];
53 | $sql = $sql[0];
54 | }
55 |
56 | // Inline sql
57 | $sql = DB::inline_parameters($sql, $parameters);
58 |
59 | // Get time and memory for the request
60 | $startTime = microtime(true);
61 | $startMemory = memory_get_usage(true);
62 |
63 | // Execute all middleware
64 | $handle = $next(...$args);
65 |
66 | // Get time and memory after the request
67 | $endTime = microtime(true);
68 | $endMemory = memory_get_usage(true);
69 |
70 | // Show query on screen
71 | if (DebugBar::getShowQueries()) {
72 | $formattedSql = DebugBarUtils::formatSql($sql);
73 | $rows = $handle->numRecords();
74 |
75 | echo '
The following query took ' . round($endTime - $startTime, 4) . 's an returned ' . $rows . " row(s) \n";
76 | echo 'Triggered by: ' . self::findSource() . '
';
77 | echo $formattedSql;
78 |
79 | // Preview results
80 | $results = iterator_to_array($handle);
81 | if ($rows > 0) {
82 | if ($rows == 1) {
83 | dump($results[0]);
84 | } else {
85 | $linearValues = count($results[0]);
86 | if ($linearValues) {
87 | dump(implode(
88 | ',',
89 | array_map(
90 | function ($item) {
91 | return $item[key($item)];
92 | },
93 | $results
94 | )
95 | ));
96 | } else {
97 | if ($rows < 20) {
98 | dump($results);
99 | } else {
100 | dump("Too many results to display");
101 | }
102 | }
103 | }
104 | }
105 | echo '
';
106 |
107 | $handle->rewind(); // Rewind the results
108 | }
109 |
110 | // Sometimes, ugly spaces are there
111 | $sql = preg_replace('/[[:blank:]]+/', ' ', trim($sql));
112 |
113 | // Sometimes, the select statement can be very long and unreadable
114 | $shortsql = $sql;
115 | $matches = null;
116 | preg_match_all('/SELECT(.+?) FROM/is', $sql, $matches);
117 | $select = empty($matches[1]) ? null : trim($matches[1][0]);
118 | if ($select !== null) {
119 | if (strlen($select) > 100) {
120 | $shortsql = str_replace($select, '"ClickToShowFields"', $sql);
121 | } else {
122 | $select = null;
123 | }
124 | }
125 |
126 | // null on the first query, since it's the select statement itself
127 | $db = DB::get_conn()->getSelectedDatabase();
128 |
129 | self::$queries[] = [
130 | 'short_query' => $shortsql,
131 | 'select' => $select,
132 | 'query' => $sql,
133 | 'start_time' => $startTime,
134 | 'end_time' => $endTime,
135 | 'duration' => $endTime - $startTime,
136 | 'memory' => $endMemory - $startMemory,
137 | 'rows' => $handle ? $handle->numRecords() : null,
138 | 'success' => $handle ? true : false,
139 | 'database' => $db,
140 | 'source' => self::$findSource ? self::findSource() : null
141 | ];
142 |
143 | return $handle;
144 | };
145 |
146 | // Attach to benchmarkQuery to fire on both query and preparedQuery
147 | $proxy = $proxy->addMethod('benchmarkQuery', $callback);
148 | }
149 |
150 | /**
151 | * Reset queries array
152 | *
153 | * Helpful for long running process and avoid accumulating queries
154 | *
155 | * @return void
156 | */
157 | public static function resetQueries()
158 | {
159 | self::$queries = [];
160 | }
161 |
162 | /**
163 | * @return array>
164 | */
165 | public static function getQueries()
166 | {
167 | return self::$queries;
168 | }
169 |
170 | /**
171 | * @param string $str
172 | * @return void
173 | */
174 | public static function addCustomQuery(string $str)
175 | {
176 | self::$queries[] = [
177 | 'short_query' => $str,
178 | 'select' => null,
179 | 'query' => $str,
180 | 'start_time' => 0,
181 | 'end_time' => 0,
182 | 'duration' => 0,
183 | 'memory' => 0,
184 | 'rows' => null,
185 | 'success' => true,
186 | 'database' => null,
187 | 'source' => null
188 | ];
189 | }
190 |
191 | /**
192 | * @return string
193 | */
194 | protected static function findSource()
195 | {
196 | $traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT);
197 |
198 | // Not relevant to determine source
199 | $internalClasses = [
200 | '',
201 | get_called_class(),
202 | // DebugBar
203 | DebugBar::class,
204 | \LeKoala\DebugBar\Middleware\DebugBarMiddleware::class,
205 | // Proxy
206 | ProxyDBExtension::class,
207 | \TractorCow\ClassProxy\Proxied\ProxiedBehaviour::class,
208 | // Orm
209 | \SilverStripe\ORM\Connect\Database::class,
210 | \SilverStripe\ORM\Connect\DBSchemaManager::class,
211 | \SilverStripe\ORM\Connect\MySQLDatabase::class,
212 | \SilverStripe\ORM\Connect\MySQLSchemaManager::class,
213 | \SilverStripe\ORM\DataObjectSchema::class,
214 | \SilverStripe\ORM\DB::class,
215 | \SilverStripe\ORM\Queries\SQLExpression::class,
216 | \SilverStripe\ORM\DataList::class,
217 | \SilverStripe\ORM\DataObject::class,
218 | \SilverStripe\ORM\DataQuery::class,
219 | \SilverStripe\ORM\Queries\SQLSelect::class,
220 | \SilverStripe\ORM\Map::class,
221 | \SilverStripe\ORM\ListDecorator::class,
222 | // Core
223 | \SilverStripe\Control\Director::class,
224 | ];
225 |
226 | $viewerClasses = [
227 | \SilverStripe\View\SSViewer_DataPresenter::class,
228 | \SilverStripe\View\SSViewer_Scope::class,
229 | \SilverStripe\View\SSViewer::class,
230 | \LeKoala\DebugBar\Proxy\SSViewerProxy::class,
231 | \SilverStripe\View\ViewableData::class
232 | ];
233 |
234 | $sources = [];
235 | foreach ($traces as $i => $trace) {
236 | // We need to be able to look ahead one item in the trace, because the class/function values
237 | // are talking about what is being *called* on this line, not the function this line lives in.
238 | if (!isset($traces[$i + 1])) {
239 | break;
240 | }
241 |
242 | $file = isset($trace['file']) ? pathinfo($trace['file'], PATHINFO_FILENAME) : null;
243 | $class = isset($traces[$i + 1]['class']) ? $traces[$i + 1]['class'] : null;
244 | $line = isset($trace['line']) ? $trace['line'] : null;
245 | $function = isset($traces[$i + 1]['function']) ? $traces[$i + 1]['function'] : null;
246 | $type = isset($traces[$i + 1]['type']) ? $traces[$i + 1]['type'] : '::';
247 |
248 | /* @var $object SSViewer */
249 | $object = isset($traces[$i + 1]['object']) ? $traces[$i + 1]['object'] : null;
250 |
251 | if (in_array($class, $internalClasses)) {
252 | continue;
253 | }
254 |
255 | // Viewer classes need special handling
256 | if (in_array($class, $viewerClasses)) {
257 | if ($function == 'includeGeneratedTemplate') {
258 | $templates = $object->templates();
259 |
260 | $template = null;
261 | if (isset($templates['main'])) {
262 | $template = basename($templates['main']);
263 | } else {
264 | $keys = array_keys($templates);
265 | $key = reset($keys);
266 | if (isset($templates[$key])) {
267 | $template = $key . ':' . basename($templates[$key]);
268 | }
269 | }
270 | if (!empty($template)) {
271 | $sources[] = $template;
272 | }
273 | }
274 | continue;
275 | }
276 |
277 | $name = $class;
278 | if ($class && !DebugBar::config()->get('show_namespaces')) {
279 | $nameArray = explode("\\", $class);
280 | $name = array_pop($nameArray);
281 |
282 | // Maybe we are inside a trait?
283 | if ($file && $file != $name) {
284 | $name .= '(' . $file . ')';
285 | }
286 | }
287 | if ($function) {
288 | $name .= $type . $function;
289 | }
290 | if ($line) {
291 | // Line number could apply to a trait
292 | $name .= ':' . $line;
293 | }
294 |
295 | $sources[] = $name;
296 |
297 | if (count($sources) > self::MAX_FIND_SOURCE_LEVEL) {
298 | break;
299 | }
300 |
301 | // We reached a Controller, exit loop
302 | if ($object && $object instanceof Controller) {
303 | break;
304 | }
305 | }
306 |
307 | if (empty($sources)) {
308 | return 'Undefined source';
309 | }
310 | return implode(' > ', $sources);
311 | }
312 | }
313 |
--------------------------------------------------------------------------------
/code/Messages/LogFormatter.php:
--------------------------------------------------------------------------------
1 | beforeRequest($request);
17 | $response = $delegate($request);
18 | if ($response) {
19 | $this->afterRequest($request, $response);
20 | }
21 | return $response;
22 | }
23 |
24 | /**
25 | * Track the start up of the framework boot
26 | *
27 | * @param HTTPRequest $request
28 | * @return void
29 | */
30 | protected function beforeRequest(HTTPRequest $request)
31 | {
32 | DebugBar::withDebugBar(function (\DebugBar\DebugBar $debugbar) {
33 | /** @var \DebugBar\DataCollector\TimeDataCollector|null $timeData */
34 | $timeData = $debugbar->getCollector('time');
35 |
36 | if (!$timeData) {
37 | return;
38 | }
39 |
40 | // Allows a more realistic view
41 | // Define FRAMEWORK_BOOT_TIME in your index.php after composer autoload
42 | if (defined('FRAMEWORK_BOOT_TIME')) {
43 | if (isset($_SERVER['REQUEST_TIME_FLOAT'])) {
44 | $timeData = $debugbar['time'];
45 | $timeData->addMeasure(
46 | 'php boot',
47 | $_SERVER['REQUEST_TIME_FLOAT'],
48 | constant('FRAMEWORK_BOOT_TIME')
49 | );
50 | $timeData->addMeasure(
51 | 'framework boot',
52 | constant('FRAMEWORK_BOOT_TIME'),
53 | microtime(true)
54 | );
55 | } else {
56 | $timeData = $debugbar['time'];
57 | $timeData->addMeasure(
58 | 'framework boot',
59 | constant('FRAMEWORK_BOOT_TIME'),
60 | microtime(true)
61 | );
62 | }
63 | } elseif (isset($_SERVER['REQUEST_TIME_FLOAT'])) {
64 | $timeData = $debugbar['time'];
65 | $timeData->addMeasure(
66 | 'framework boot',
67 | $_SERVER['REQUEST_TIME_FLOAT'],
68 | microtime(true)
69 | );
70 | }
71 |
72 | DebugBar::closeExtraTime();
73 | $timeData->startMeasure('pre_request', 'pre request');
74 | });
75 | }
76 |
77 | /**
78 | * Inject DebugBar requirements for the frontend
79 | *
80 | * @param HTTPRequest $request
81 | * @param HTTPResponse $response
82 | * @return void
83 | */
84 | protected function afterRequest(HTTPRequest $request, HTTPResponse $response)
85 | {
86 | $debugbar = DebugBar::getDebugBar();
87 | if (!$debugbar) {
88 | return;
89 | }
90 |
91 | // Don't apply to assets
92 | $dir = defined('ASSETS_DIR') ? ASSETS_DIR : 'assets';
93 | if (strpos($request->getURL(), "$dir/") === 0) {
94 | return;
95 | }
96 |
97 | DebugBar::setRequest($request);
98 |
99 | // All queries have been displayed
100 | if (DebugBar::getShowQueries()) {
101 | exit();
102 | }
103 | $script = DebugBar::renderDebugBar();
104 |
105 | // If the bar is not renderable, return early
106 | if (!$script) {
107 | return;
108 | }
109 |
110 | // Use module to make sure it's deferred and does not cause side effects
111 | $script = str_replace('