first
This commit is contained in:
339
wp-content/plugins/query-monitor/LICENSE
Normal file
339
wp-content/plugins/query-monitor/LICENSE
Normal file
@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
{{description}}
|
||||
Copyright (C) {{year}} {{fullname}}
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
{signature of Ty Coon}, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
@ -0,0 +1 @@
|
||||
<path d="M18 12h-2.18c-0.17 0.7-0.44 1.35-0.81 1.93l1.54 1.54-2.1 2.1-1.54-1.54c-0.58 0.36-1.23 0.63-1.91 0.79v2.18h-3v-2.18c-0.68-0.16-1.33-0.43-1.91-0.79l-1.54 1.54-2.12-2.12 1.54-1.54c-0.36-0.58-0.63-1.23-0.79-1.91h-2.18v-2.97h2.17c0.16-0.7 0.44-1.35 0.8-1.94l-1.54-1.54 2.1-2.1 1.54 1.54c0.58-0.37 1.24-0.64 1.93-0.81v-2.18h3v2.18c0.68 0.16 1.33 0.43 1.91 0.79l1.54-1.54 2.12 2.12-1.54 1.54c0.36 0.59 0.64 1.24 0.8 1.94h2.17v2.97zM9.5 13.5c1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3 1.34 3 3 3z"></path>
|
@ -0,0 +1 @@
|
||||
<path d="M15 8l-4.030 6-3.97-6h8z"></path>
|
1
wp-content/plugins/query-monitor/assets/icons/edit.svg
Normal file
1
wp-content/plugins/query-monitor/assets/icons/edit.svg
Normal file
@ -0,0 +1 @@
|
||||
<path d="M13.89 3.39l2.71 2.72c0.46 0.46 0.42 1.24 0.030 1.64l-8.010 8.020-5.56 1.16 1.16-5.58s7.6-7.63 7.99-8.030c0.39-0.39 1.22-0.39 1.68 0.070zM11.16 6.18l-5.59 5.61 1.11 1.11 5.54-5.65zM8.19 14.41l5.58-5.6-1.070-1.080-5.59 5.6z"></path>
|
@ -0,0 +1 @@
|
||||
<path d="M9 3h8v8l-2-1v-3.080l-5.6 5.59-1.41-1.41 6.090-6.1h-4.080zM12 15v-3l2-2v7h-11v-11h8l-2 2h-4v7h7z"></path>
|
1
wp-content/plugins/query-monitor/assets/icons/filter.svg
Normal file
1
wp-content/plugins/query-monitor/assets/icons/filter.svg
Normal file
@ -0,0 +1 @@
|
||||
<path d="M3 4.5v-2s3.34-1 7-1 7 1 7 1v2l-5 7.030v6.97s-1.22-0.090-2.25-0.59-1.75-1.41-1.75-1.41v-4.97z"></path>
|
@ -0,0 +1 @@
|
||||
<path d="M7 5h-1.95c0-1.74 0.85-2.9 2.95-2.9v-2.1c-3.15 0-5.040 2.11-5.040 5h-1.78l2.62 3.39zM20 1v14h-5v5h-14v-10h9v-9h10zM18 3h-6v7h3v3h3v-10zM13 12h-10v6h10v-6z"></path>
|
@ -0,0 +1 @@
|
||||
<path d="M15.95 5h-1.95l3.2 3.39 2.62-3.39h-1.78c0-2.89-1.89-5-5.040-5v2.1c2.1 0 2.95 1.16 2.95 2.9zM1 1h10v9h9v10h-14v-5h-5v-14zM3 3v10h3v-3h3v-7h-6zM8 12v6h10v-6h-10z"></path>
|
1
wp-content/plugins/query-monitor/assets/icons/info.svg
Normal file
1
wp-content/plugins/query-monitor/assets/icons/info.svg
Normal file
@ -0,0 +1 @@
|
||||
<path d="M10 2c4.42 0 8 3.58 8 8s-3.58 8-8 8-8-3.58-8-8 3.58-8 8-8zM11 6c0-0.55-0.45-1-1-1s-1 0.45-1 1 0.45 1 1 1 1-0.45 1-1zM11 15v-6h-2v6h2z"></path>
|
1
wp-content/plugins/query-monitor/assets/icons/no-alt.svg
Normal file
1
wp-content/plugins/query-monitor/assets/icons/no-alt.svg
Normal file
@ -0,0 +1 @@
|
||||
<path d="M14.95 6.46l-3.54 3.54 3.54 3.54-1.41 1.41-3.54-3.53-3.53 3.53-1.42-1.42 3.53-3.53-3.53-3.53 1.42-1.42 3.53 3.53 3.54-3.53z"></path>
|
@ -0,0 +1 @@
|
||||
<path d="M10 2c4.42 0 8 3.58 8 8s-3.58 8-8 8-8-3.58-8-8 3.58-8 8-8zM11.13 11.38l0.35-6.46h-2.96l0.35 6.46h2.26zM11.040 14.74c0.24-0.23 0.37-0.55 0.37-0.96 0-0.42-0.12-0.74-0.36-0.97s-0.59-0.35-1.060-0.35-0.82 0.12-1.070 0.35-0.37 0.55-0.37 0.97c0 0.41 0.13 0.73 0.38 0.96 0.26 0.23 0.61 0.34 1.060 0.34s0.8-0.11 1.050-0.34z"></path>
|
@ -0,0 +1,2 @@
|
||||
<path d="M10,2c-4.42,0-8,3.58-8,8s3.58,8,8,8s8-3.58,8-8S14.42,2,10,2z M9.385,14.66h-1.34l-3.24-4.54l1.341-1.25l2.569,2.4
|
||||
l5.141-5.931l1.34,0.94L9.385,14.66z"/>
|
1486
wp-content/plugins/query-monitor/assets/query-monitor.css
Normal file
1486
wp-content/plugins/query-monitor/assets/query-monitor.css
Normal file
File diff suppressed because it is too large
Load Diff
791
wp-content/plugins/query-monitor/assets/query-monitor.js
Normal file
791
wp-content/plugins/query-monitor/assets/query-monitor.js
Normal file
@ -0,0 +1,791 @@
|
||||
/**
|
||||
* Front-end functionality for Query Monitor.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
var QM_i18n = {
|
||||
|
||||
// http://core.trac.wordpress.org/ticket/20491
|
||||
|
||||
number_format : function( number, decimals ) {
|
||||
|
||||
if ( isNaN( number ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! decimals ) {
|
||||
decimals = 0;
|
||||
}
|
||||
|
||||
number = parseFloat( number );
|
||||
|
||||
var num_float = number.toFixed( decimals ),
|
||||
num_int = Math.floor( number ),
|
||||
num_str = num_int.toString(),
|
||||
fraction = num_float.substring( num_float.indexOf( '.' ) + 1, num_float.length ),
|
||||
o = '';
|
||||
|
||||
if ( num_str.length > 3 ) {
|
||||
for ( i = num_str.length; i > 3; i -= 3 ) {
|
||||
o = qm_number_format.thousands_sep + num_str.slice( i - 3, i ) + o;
|
||||
}
|
||||
o = num_str.slice( 0, i ) + o;
|
||||
} else {
|
||||
o = num_str;
|
||||
}
|
||||
|
||||
if ( decimals ) {
|
||||
o = o + qm_number_format.decimal_point + fraction;
|
||||
}
|
||||
|
||||
return o;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
if ( window.jQuery ) {
|
||||
|
||||
jQuery( function($) {
|
||||
var toolbarHeight = $('#wpadminbar').length ? $('#wpadminbar').outerHeight() : 0;
|
||||
var minheight = 100;
|
||||
var maxheight = ( $(window).height() - toolbarHeight );
|
||||
var minwidth = 300;
|
||||
var maxwidth = $(window).width();
|
||||
var container = $('#query-monitor-main');
|
||||
var body = $('body');
|
||||
var body_margin = body.css('margin-bottom');
|
||||
var container_height_key = 'qm-container-height';
|
||||
var container_pinned_key = 'qm-' + ( $('body').hasClass('wp-admin') ? 'admin' : 'front' ) + '-container-pinned';
|
||||
var container_position_key = 'qm-container-position';
|
||||
var container_width_key = 'qm-container-width';
|
||||
|
||||
if ( container.hasClass('qm-peek') ) {
|
||||
minheight = 27;
|
||||
}
|
||||
|
||||
container.removeClass('qm-no-js').addClass('qm-js');
|
||||
|
||||
var theme = localStorage.getItem( 'qm-theme' );
|
||||
if ( theme ) {
|
||||
container.attr('data-theme', theme);
|
||||
$('.qm-theme-toggle[value="' + theme + '"]').prop('checked', true);
|
||||
}
|
||||
|
||||
if ( $('#qm-fatal').length ) {
|
||||
console.error(qm_l10n.fatal_error + ': ' + $('#qm-fatal').attr('data-qm-message') );
|
||||
|
||||
if ( $('#wp-admin-bar-query-monitor').length ) {
|
||||
$('#wp-admin-bar-query-monitor')
|
||||
.addClass('qm-error')
|
||||
.find('a').eq(0)
|
||||
.text(qm_l10n.fatal_error);
|
||||
|
||||
var fatal_container = document.createDocumentFragment();
|
||||
|
||||
var fatal_message_menu = $('#wp-admin-bar-query-monitor-placeholder')
|
||||
.clone()
|
||||
.attr('id','wp-admin-bar-qm-fatal-message');
|
||||
|
||||
fatal_message_menu
|
||||
.find('a').eq(0)
|
||||
.text($('#qm-fatal').attr('data-qm-message'))
|
||||
.attr('href','#qm-fatal');
|
||||
|
||||
fatal_container.appendChild( fatal_message_menu.get(0) );
|
||||
|
||||
var fatal_file_menu = $('#wp-admin-bar-query-monitor-placeholder')
|
||||
.clone()
|
||||
.attr('id','wp-admin-bar-qm-fatal-file');
|
||||
|
||||
fatal_file_menu
|
||||
.find('a').eq(0)
|
||||
.text($('#qm-fatal').attr('data-qm-file') + ':' + $('#qm-fatal').attr('data-qm-line'))
|
||||
.attr('href','#qm-fatal');
|
||||
|
||||
fatal_container.appendChild( fatal_file_menu.get(0) );
|
||||
|
||||
$('#wp-admin-bar-query-monitor ul').append(fatal_container);
|
||||
}
|
||||
}
|
||||
|
||||
var link_click = function(e){
|
||||
var href = $( this ).attr('href') || $( this ).data('qm-href');
|
||||
|
||||
if ( '#qm-fatal' === href ) {
|
||||
return;
|
||||
}
|
||||
|
||||
show_panel( href );
|
||||
$(href).focus();
|
||||
$('#wp-admin-bar-query-monitor').removeClass('hover');
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
var stripes = function( table ) {
|
||||
table.each(function() {
|
||||
$(this).find('tbody tr').removeClass('qm-odd').not('[class*="qm-hide-"]').filter(':even').addClass('qm-odd');
|
||||
} );
|
||||
};
|
||||
|
||||
var show_panel = function( panel ) {
|
||||
container.addClass('qm-show').removeClass('qm-hide');
|
||||
$( '.qm' ).removeClass('qm-panel-show');
|
||||
$('#qm-panels').scrollTop(0);
|
||||
$( panel ).addClass('qm-panel-show');
|
||||
|
||||
if ( container.height() < minheight ) {
|
||||
container.height( minheight );
|
||||
}
|
||||
|
||||
if ( container.hasClass('qm-show-right') ) {
|
||||
body.css( 'margin-bottom', '' );
|
||||
} else {
|
||||
body.css( 'margin-bottom', 'calc( ' + body_margin + ' + ' + container.height() + 'px )' );
|
||||
}
|
||||
|
||||
$('#qm-panel-menu').find('button').removeAttr('aria-selected');
|
||||
$('#qm-panel-menu').find('li').removeClass('qm-current-menu');
|
||||
var selected_menu = $('#qm-panel-menu').find('[data-qm-href="' + panel + '"]').attr('aria-selected',true);
|
||||
|
||||
if ( selected_menu.length ) {
|
||||
var selected_menu_top = selected_menu.position().top - 27;
|
||||
var menu_height = $('#qm-panel-menu').height();
|
||||
var menu_scroll = $('#qm-panel-menu').scrollTop();
|
||||
selected_menu.closest('#qm-panel-menu > ul > li').addClass('qm-current-menu');
|
||||
|
||||
var selected_menu_off_bottom = ( selected_menu_top > ( menu_height ) );
|
||||
var selected_menu_off_top = ( selected_menu_top < 0 );
|
||||
|
||||
if ( selected_menu_off_bottom || selected_menu_off_top ) {
|
||||
$('#qm-panel-menu').scrollTop( selected_menu_top + menu_scroll - ( menu_height / 2 ) + ( selected_menu.outerHeight() / 2 ) );
|
||||
}
|
||||
}
|
||||
|
||||
$('.qm-title-heading select').val(panel);
|
||||
|
||||
localStorage.setItem( container_pinned_key, panel );
|
||||
|
||||
var filters = $( panel ).find('.qm-filter');
|
||||
|
||||
if ( filters.length ) {
|
||||
filters.trigger('change');
|
||||
} else {
|
||||
stripes( $(panel).find('table') );
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
if ( $('#wp-admin-bar-query-monitor').length ) {
|
||||
|
||||
var admin_bar_menu_container = document.createDocumentFragment();
|
||||
|
||||
if ( window.qm && window.qm.menu ) {
|
||||
$('#wp-admin-bar-query-monitor')
|
||||
.addClass(qm.menu.top.classname)
|
||||
.attr('dir','ltr')
|
||||
.find('a').eq(0)
|
||||
.html(qm.menu.top.title);
|
||||
|
||||
$.each( qm.menu.sub, function( i, el ) {
|
||||
|
||||
var new_menu = $('#wp-admin-bar-query-monitor-placeholder')
|
||||
.clone()
|
||||
.attr('id','wp-admin-bar-' + el.id);
|
||||
new_menu
|
||||
.find('a').eq(0)
|
||||
.html(el.title)
|
||||
.attr('href',el.href);
|
||||
|
||||
if ( ( typeof el.meta != 'undefined' ) && ( typeof el.meta.classname != 'undefined' ) ) {
|
||||
new_menu.addClass(el.meta.classname);
|
||||
}
|
||||
|
||||
admin_bar_menu_container.appendChild( new_menu.get(0) );
|
||||
|
||||
} );
|
||||
|
||||
$('#wp-admin-bar-query-monitor ul').append(admin_bar_menu_container);
|
||||
}
|
||||
|
||||
$('#wp-admin-bar-query-monitor').find('a').on('click',link_click);
|
||||
|
||||
$('#wp-admin-bar-query-monitor,#wp-admin-bar-query-monitor-default').show();
|
||||
|
||||
} else {
|
||||
container.addClass('qm-peek').removeClass('qm-hide');
|
||||
$('#qm-overview').addClass('qm-panel-show');
|
||||
}
|
||||
|
||||
$('#qm-panel-menu').find('button').on('click',link_click);
|
||||
|
||||
container.find('.qm-filter').on('change',function(e){
|
||||
|
||||
var filter = $(this).attr('data-filter'),
|
||||
table = $(this).closest('table'),
|
||||
tr = table.find('tbody tr[data-qm-' + filter + ']'),
|
||||
// Escape the following chars with a backslash before passing into jQ selectors: [ ] ( ) ' " \
|
||||
val = $(this).val().replace(/[[\]()'"\\]/g, "\\$&"),
|
||||
total = tr.removeClass('qm-hide-' + filter).length,
|
||||
hilite = $(this).attr('data-highlight'),
|
||||
time = 0;
|
||||
|
||||
key = $(this).attr('id');
|
||||
if ( val ) {
|
||||
sessionStorage.setItem( key, $(this).val() );
|
||||
} else {
|
||||
sessionStorage.removeItem( key );
|
||||
}
|
||||
|
||||
if ( hilite ) {
|
||||
table.find('tr').removeClass('qm-highlight');
|
||||
}
|
||||
|
||||
if ( $(this).val() !== '' ) {
|
||||
if ( hilite ) {
|
||||
tr.filter('[data-qm-' + hilite + '*="' + val + '"]').addClass('qm-highlight');
|
||||
}
|
||||
tr.not('[data-qm-' + filter + '*="' + val + '"]').addClass('qm-hide-' + filter);
|
||||
$(this).closest('th').addClass('qm-filtered');
|
||||
} else {
|
||||
$(this).closest('th').removeClass('qm-filtered');
|
||||
}
|
||||
|
||||
var matches = tr.filter(':visible');
|
||||
var filtered_count = 0;
|
||||
var total_count = 0;
|
||||
matches.each(function(i){
|
||||
var row_time = $(this).attr('data-qm-time');
|
||||
if ( row_time ) {
|
||||
time += parseFloat( row_time );
|
||||
}
|
||||
|
||||
var row_count = $(this).attr('data-qm-count');
|
||||
if ( row_count ) {
|
||||
filtered_count += parseFloat( row_count );
|
||||
} else {
|
||||
filtered_count++;
|
||||
}
|
||||
});
|
||||
if ( time ) {
|
||||
time = QM_i18n.number_format( time, 4 );
|
||||
}
|
||||
|
||||
tr.each(function(i){
|
||||
var row_count = $(this).attr('data-qm-count');
|
||||
if ( row_count ) {
|
||||
total_count += parseFloat( row_count );
|
||||
} else {
|
||||
total_count++;
|
||||
}
|
||||
});
|
||||
|
||||
if ( table.find('.qm-filtered').length ) {
|
||||
var count = filtered_count + ' / ' + total_count;
|
||||
} else {
|
||||
var count = filtered_count;
|
||||
}
|
||||
|
||||
table.find('.qm-items-number').text(count);
|
||||
table.find('.qm-items-time').text(time);
|
||||
|
||||
stripes(table);
|
||||
});
|
||||
|
||||
container.find('.qm-filter').each(function () {
|
||||
var key = $(this).attr('id');
|
||||
var value = sessionStorage.getItem( key );
|
||||
if ( value !== null ) {
|
||||
// Escape the following chars with a backslash before passing into jQ selectors: [ ] ( ) ' " \
|
||||
var val = value.replace(/[[\]()'"\\]/g, "\\$&");
|
||||
if ( ! $(this).find('option[value="' + val + '"]').length ) {
|
||||
$('<option>').attr('value',value).text(value).appendTo(this);
|
||||
}
|
||||
$(this).val(value).trigger('change');
|
||||
}
|
||||
});
|
||||
|
||||
container.find('.qm-filter-trigger').on('click',function(e){
|
||||
var filter = $(this).data('qm-filter'),
|
||||
value = $(this).data('qm-value'),
|
||||
target = $(this).data('qm-target');
|
||||
$('#qm-' + target).find('.qm-filter').not('[data-filter="' + filter + '"]').val('').removeClass('qm-highlight').trigger('change');
|
||||
$('#qm-' + target).find('[data-filter="' + filter + '"]').val(value).addClass('qm-highlight').trigger('change');
|
||||
show_panel( '#qm-' + target );
|
||||
$('#qm-' + target).focus();
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
container.find('.qm-toggle').on('click',function(e){
|
||||
var el = $(this);
|
||||
var currentState = el.attr('aria-expanded');
|
||||
var newState = 'true';
|
||||
if (currentState === 'true') {
|
||||
newState = 'false';
|
||||
}
|
||||
el.attr('aria-expanded', newState);
|
||||
var toggle = $(this).closest('td').find('.qm-toggled');
|
||||
if ( currentState === 'true' ) {
|
||||
if ( toggle.length ) {
|
||||
toggle.slideToggle(150,function(){
|
||||
el.closest('td').removeClass('qm-toggled-on');
|
||||
el.text(el.attr('data-on'));
|
||||
});
|
||||
} else {
|
||||
el.closest('td').removeClass('qm-toggled-on');
|
||||
el.text(el.attr('data-on'));
|
||||
}
|
||||
} else {
|
||||
el.closest('td').addClass('qm-toggled-on');
|
||||
el.text(el.attr('data-off'));
|
||||
toggle.slideToggle(150);
|
||||
}
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
container.find('.qm-highlighter').on('mouseenter',function(e){
|
||||
|
||||
var subject = $(this).data('qm-highlight');
|
||||
var table = $(this).closest('table');
|
||||
|
||||
if ( ! subject ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$(this).addClass('qm-highlight');
|
||||
|
||||
$.each( subject.split(' '), function( i, el ){
|
||||
table.find('tr[data-qm-subject="' + el + '"]').addClass('qm-highlight');
|
||||
});
|
||||
|
||||
}).on('mouseleave',function(e){
|
||||
|
||||
$(this).removeClass('qm-highlight');
|
||||
$(this).closest('table').find('tr').removeClass('qm-highlight');
|
||||
|
||||
});
|
||||
|
||||
$('.qm').find('tbody a,tbody button').on('focus',function(e){
|
||||
$(this).closest('tr').addClass('qm-hovered');
|
||||
}).on('blur',function(e){
|
||||
$(this).closest('tr').removeClass('qm-hovered');
|
||||
});
|
||||
|
||||
container.find('.qm table').on('sorted.qm',function(){
|
||||
stripes( $(this) );
|
||||
});
|
||||
|
||||
$( document ).ajaxSuccess( function( event, response, options ) {
|
||||
|
||||
var errors = response.getResponseHeader( 'X-QM-php_errors-error-count' );
|
||||
|
||||
if ( ! errors ) {
|
||||
return event;
|
||||
}
|
||||
|
||||
errors = parseInt( errors, 10 );
|
||||
|
||||
if ( window.console ) {
|
||||
console.group( qm_l10n.ajax_error );
|
||||
}
|
||||
|
||||
for ( var key = 1; key <= errors; key++ ) {
|
||||
|
||||
error = JSON.parse( response.getResponseHeader( 'X-QM-php_errors-error-' + key ) );
|
||||
|
||||
if ( window.console ) {
|
||||
switch ( error.type ) {
|
||||
case 'warning':
|
||||
console.error( error );
|
||||
break;
|
||||
default:
|
||||
console.warn( error );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $('#qm-php_errors').find('[data-qm-key="' + error.key + '"]').length ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( $('#wp-admin-bar-query-monitor').length ) {
|
||||
if ( ! qm.ajax_errors[error.type] ) {
|
||||
$('#wp-admin-bar-query-monitor')
|
||||
.addClass('qm-' + error.type)
|
||||
.find('a').first().append('<span class="ab-label qm-ajax-' + error.type + '"> Ajax: ' + error.type + '</span>');
|
||||
}
|
||||
}
|
||||
|
||||
qm.ajax_errors[error.type] = true;
|
||||
|
||||
}
|
||||
|
||||
if ( window.console ) {
|
||||
console.groupEnd();
|
||||
}
|
||||
|
||||
$( '#qm-ajax-errors' ).show();
|
||||
|
||||
return event;
|
||||
|
||||
} );
|
||||
|
||||
$('.qm-auth').on('click',function(e){
|
||||
var state = $('#qm-settings').data('qm-state');
|
||||
var action = ( 'off' === state ? 'on' : 'off' );
|
||||
|
||||
$.ajax(qm_l10n.ajaxurl,{
|
||||
type : 'POST',
|
||||
context : this,
|
||||
data : {
|
||||
action : 'qm_auth_' + action,
|
||||
nonce : qm_l10n.auth_nonce[action]
|
||||
},
|
||||
success : function(response){
|
||||
$(this).text( $(this).data('qm-text-' + action) );
|
||||
$('#qm-settings').attr('data-qm-state',action).data('qm-state',action);
|
||||
},
|
||||
dataType : 'json',
|
||||
xhrFields: {
|
||||
withCredentials: true
|
||||
}
|
||||
});
|
||||
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
var editorSuccessIndicator = $('#qm-editor-save-status');
|
||||
editorSuccessIndicator.hide();
|
||||
|
||||
$('.qm-editor-button').on('click',function(e){
|
||||
var state = $('#qm-settings').data('qm-state');
|
||||
var editor = $('#qm-editor-select').val();
|
||||
|
||||
$.ajax(qm_l10n.ajaxurl,{
|
||||
type : 'POST',
|
||||
context : this,
|
||||
data : {
|
||||
action : 'qm_editor_set',
|
||||
nonce : qm_l10n.auth_nonce['editor-set'],
|
||||
editor : editor
|
||||
},
|
||||
success : function(response){
|
||||
if (response.success) {
|
||||
editorSuccessIndicator.show();
|
||||
}
|
||||
},
|
||||
dataType : 'json',
|
||||
xhrFields: {
|
||||
withCredentials: true
|
||||
}
|
||||
});
|
||||
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
$('.qm-theme-toggle').on('click',function(e){
|
||||
container.attr('data-theme',$(this).val());
|
||||
localStorage.setItem('qm-theme',$(this).val());
|
||||
});
|
||||
|
||||
$.qm.tableSort({target: $('.qm-sortable')});
|
||||
|
||||
var startY, startX, resizerHeight;
|
||||
|
||||
$(document).on('mousedown touchstart', '.qm-resizer', function(event) {
|
||||
event.stopPropagation();
|
||||
|
||||
resizerHeight = $(this).outerHeight() - 1;
|
||||
startY = container.outerHeight() + ( event.clientY || event.originalEvent.targetTouches[0].pageY );
|
||||
startX = container.outerWidth() + ( event.clientX || event.originalEvent.targetTouches[0].pageX );
|
||||
|
||||
if ( ! container.hasClass('qm-show-right') ) {
|
||||
$(document).on('mousemove touchmove', qm_do_resizer_drag_vertical);
|
||||
} else {
|
||||
$(document).on('mousemove touchmove', qm_do_resizer_drag_horizontal);
|
||||
}
|
||||
|
||||
$(document).on('mouseup touchend', qm_stop_resizer_drag);
|
||||
});
|
||||
|
||||
function qm_do_resizer_drag_vertical(event) {
|
||||
var h = ( startY - ( event.clientY || event.originalEvent.targetTouches[0].pageY ) );
|
||||
if ( h >= resizerHeight && h <= maxheight ) {
|
||||
container.height( h );
|
||||
body.css( 'margin-bottom', 'calc( ' + body_margin + ' + ' + h + 'px )' );
|
||||
}
|
||||
}
|
||||
|
||||
function qm_do_resizer_drag_horizontal(event) {
|
||||
var w = ( startX - event.clientX );
|
||||
if ( w >= minwidth && w <= maxwidth ) {
|
||||
container.width( w );
|
||||
}
|
||||
body.css( 'margin-bottom', '' );
|
||||
}
|
||||
|
||||
function qm_stop_resizer_drag(event) {
|
||||
$(document).off('mousemove touchmove', qm_do_resizer_drag_vertical);
|
||||
$(document).off('mousemove touchmove', qm_do_resizer_drag_horizontal);
|
||||
$(document).off('mouseup touchend', qm_stop_resizer_drag);
|
||||
|
||||
if ( ! container.hasClass('qm-show-right') ) {
|
||||
localStorage.removeItem( container_position_key );
|
||||
localStorage.setItem( container_height_key, container.height() );
|
||||
} else {
|
||||
localStorage.setItem( container_position_key, 'right' );
|
||||
localStorage.setItem( container_width_key, container.width() );
|
||||
}
|
||||
}
|
||||
|
||||
var p = localStorage.getItem( container_position_key );
|
||||
var h = localStorage.getItem( container_height_key );
|
||||
var w = localStorage.getItem( container_width_key );
|
||||
if ( ! container.hasClass('qm-peek') ) {
|
||||
if ( p === 'right' ) {
|
||||
if ( w !== null ) {
|
||||
if ( w < minwidth ) {
|
||||
w = minwidth;
|
||||
}
|
||||
if ( w > maxwidth ) {
|
||||
w = maxwidth;
|
||||
}
|
||||
container.width( w );
|
||||
}
|
||||
container.addClass('qm-show-right');
|
||||
} else if ( p !== 'right' && h !== null ) {
|
||||
if ( h < minheight ) {
|
||||
h = minheight;
|
||||
}
|
||||
if ( h > maxheight ) {
|
||||
h = maxheight;
|
||||
}
|
||||
container.height( h );
|
||||
}
|
||||
}
|
||||
|
||||
$(window).on('resize', function(){
|
||||
var h = container.height();
|
||||
var w = container.width();
|
||||
|
||||
maxheight = ( $(window).height() - toolbarHeight );
|
||||
maxwidth = $(window).width();
|
||||
|
||||
if ( h < minheight ) {
|
||||
container.height( minheight );
|
||||
}
|
||||
if ( h > maxheight ) {
|
||||
container.height( maxheight );
|
||||
}
|
||||
localStorage.setItem( container_height_key, container.height() );
|
||||
|
||||
if ( w > $(window).width() ) {
|
||||
container.width( minwidth );
|
||||
localStorage.setItem( container_width_key, container.width() );
|
||||
}
|
||||
if ( $(window).width() < 960 ) {
|
||||
container.removeClass('qm-show-right');
|
||||
localStorage.removeItem( container_position_key );
|
||||
}
|
||||
});
|
||||
|
||||
$('.qm-button-container-close').on('click',function(){
|
||||
container.removeClass('qm-show').height('').width('');
|
||||
body.css( 'margin-bottom', '' );
|
||||
localStorage.removeItem( container_pinned_key );
|
||||
});
|
||||
|
||||
$('.qm-button-container-settings,a[href="#qm-settings"]').on('click',function(){
|
||||
show_panel( '#qm-settings' );
|
||||
$('#qm-settings').focus();
|
||||
});
|
||||
|
||||
$('.qm-button-container-position').on('click',function(){
|
||||
container.toggleClass('qm-show-right');
|
||||
|
||||
if ( container.hasClass('qm-show-right') ) {
|
||||
var w = localStorage.getItem( container_width_key );
|
||||
|
||||
if ( w !== null && w < $(window).width() ) {
|
||||
container.width( w );
|
||||
}
|
||||
|
||||
body.css( 'margin-bottom', '' );
|
||||
|
||||
localStorage.setItem( container_position_key, 'right' );
|
||||
} else {
|
||||
body.css( 'margin-bottom', 'calc( ' + body_margin + ' + ' + container.height() + 'px )' );
|
||||
|
||||
localStorage.removeItem( container_position_key );
|
||||
}
|
||||
});
|
||||
|
||||
var pinned = localStorage.getItem( container_pinned_key );
|
||||
if ( pinned && $( pinned ).length ) {
|
||||
show_panel( pinned );
|
||||
}
|
||||
|
||||
$('.qm-title-heading select').on('change',function(){
|
||||
show_panel( $(this).val() );
|
||||
$($(this).val()).focus();
|
||||
});
|
||||
|
||||
} );
|
||||
|
||||
/**
|
||||
* Table sorting library.
|
||||
*
|
||||
* This is a modified version of jQuery table-sort v0.1.1
|
||||
* https://github.com/gajus/table-sort
|
||||
*
|
||||
* Licensed under the BSD.
|
||||
* https://github.com/gajus/table-sort/blob/master/LICENSE
|
||||
*
|
||||
* Author: Gajus Kuizinas <g.kuizinas@anuary.com>
|
||||
*/
|
||||
(function ($) {
|
||||
$.qm = $.qm || {};
|
||||
$.qm.tableSort = function (settings) {
|
||||
// @param object columns NodeList table columns.
|
||||
// @param integer row_width defines the number of columns per row.
|
||||
var table_to_array = function (columns, row_width) {
|
||||
columns = Array.prototype.slice.call(columns, 0);
|
||||
|
||||
var rows = [];
|
||||
var row_index = 0;
|
||||
|
||||
for (var i = 0, j = columns.length; i < j; i += row_width) {
|
||||
var row = [];
|
||||
|
||||
for (var k = 0; k < row_width; k++) {
|
||||
var e = columns[i + k];
|
||||
var data = e.dataset.qmSortWeight;
|
||||
|
||||
if (data === undefined) {
|
||||
data = e.textContent || e.innerText;
|
||||
}
|
||||
|
||||
var number = parseFloat(data);
|
||||
|
||||
data = isNaN(number) ? data : number;
|
||||
|
||||
row.push(data);
|
||||
}
|
||||
|
||||
rows.push({index: row_index++, data: row});
|
||||
}
|
||||
|
||||
return rows;
|
||||
};
|
||||
|
||||
if ( ! settings.target || ! ( settings.target instanceof $) ) {
|
||||
throw 'Target is not defined or it is not instance of jQuery.';
|
||||
}
|
||||
|
||||
settings.target.each(function () {
|
||||
var table = $(this);
|
||||
|
||||
table.find('.qm-sortable-column').on('click', function (e) {
|
||||
var desc = ! $(this).hasClass('qm-sorted-desc');
|
||||
var index = $(this).index();
|
||||
|
||||
table.find('thead th').removeClass('qm-sorted-asc qm-sorted-desc').removeAttr('aria-sort');
|
||||
|
||||
if ( desc ) {
|
||||
$(this).addClass('qm-sorted-desc').attr('aria-sort','descending');
|
||||
} else {
|
||||
$(this).addClass('qm-sorted-asc').attr('aria-sort','ascending');
|
||||
}
|
||||
|
||||
table.find('tbody').each(function () {
|
||||
var tbody = $(this);
|
||||
var rows = this.rows;
|
||||
var columns = this.querySelectorAll('th,td');
|
||||
|
||||
if (this.data_matrix === undefined) {
|
||||
this.data_matrix = table_to_array(columns, $(rows[0]).find('th,td').length);
|
||||
}
|
||||
|
||||
var data = this.data_matrix;
|
||||
|
||||
data.sort(function (a, b) {
|
||||
if (a.data[index] == b.data[index]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (desc ? a.data[index] > b.data[index] : a.data[index] < b.data[index]) ? -1 : 1;
|
||||
});
|
||||
|
||||
// Detach the tbody to prevent unnecessary overhead related
|
||||
// to the browser environment.
|
||||
tbody = tbody.detach();
|
||||
|
||||
// Convert NodeList into an array.
|
||||
rows = Array.prototype.slice.call(rows, 0);
|
||||
|
||||
var last_row = rows[data[data.length - 1].index];
|
||||
|
||||
for (var i = 0, j = data.length - 1; i < j; i++) {
|
||||
tbody[0].insertBefore(rows[data[i].index], last_row);
|
||||
|
||||
// Restore the index.
|
||||
data[i].index = i;
|
||||
}
|
||||
|
||||
// Restore the index.
|
||||
data[data.length - 1].index = data.length - 1;
|
||||
|
||||
table.append(tbody);
|
||||
});
|
||||
|
||||
table.trigger('sorted.qm');
|
||||
|
||||
e.preventDefault();
|
||||
});
|
||||
});
|
||||
};
|
||||
})(jQuery);
|
||||
|
||||
}
|
||||
|
||||
window.addEventListener('load', function() {
|
||||
var main = document.getElementById( 'query-monitor-main' );
|
||||
var ceased = document.getElementById( 'query-monitor-ceased' );
|
||||
var broken = document.getElementById( 'qm-broken' );
|
||||
var menu_item = document.getElementById( 'wp-admin-bar-query-monitor' );
|
||||
|
||||
if ( ( 'undefined' === typeof jQuery ) || ! window.jQuery ) {
|
||||
/* Fallback for running without jQuery (`QM_NO_JQUERY`) or when jQuery is broken */
|
||||
|
||||
if ( main ) {
|
||||
main.className += ' qm-broken';
|
||||
}
|
||||
|
||||
if ( broken ) {
|
||||
console.error( broken.textContent );
|
||||
}
|
||||
|
||||
if ( 'undefined' === typeof jQuery ) {
|
||||
console.error( 'QM error from JS: undefined jQuery' );
|
||||
} else if ( ! window.jQuery ) {
|
||||
console.error( 'QM error from JS: no jQuery' );
|
||||
}
|
||||
|
||||
if ( menu_item && main ) {
|
||||
menu_item.addEventListener( 'click', function() {
|
||||
main.className += ' qm-show';
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! main ) {
|
||||
if ( ceased ) {
|
||||
// QM was ceased
|
||||
console.info( 'QM: collection and output was ceased' );
|
||||
} else {
|
||||
// QM's output has disappeared
|
||||
console.error( 'QM error from JS: QM output does not exist' );
|
||||
}
|
||||
}
|
||||
} );
|
134
wp-content/plugins/query-monitor/classes/Activation.php
Normal file
134
wp-content/plugins/query-monitor/classes/Activation.php
Normal file
@ -0,0 +1,134 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Plugin activation handler.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_Activation extends QM_Plugin {
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
*/
|
||||
protected function __construct( $file ) {
|
||||
# Filters
|
||||
add_filter( 'pre_update_option_active_plugins', array( $this, 'filter_active_plugins' ) );
|
||||
add_filter( 'pre_update_site_option_active_sitewide_plugins', array( $this, 'filter_active_sitewide_plugins' ) );
|
||||
|
||||
# Activation and deactivation
|
||||
register_activation_hook( $file, array( $this, 'activate' ) );
|
||||
register_deactivation_hook( $file, array( $this, 'deactivate' ) );
|
||||
|
||||
# Parent setup:
|
||||
parent::__construct( $file );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $sitewide
|
||||
* @return void
|
||||
*/
|
||||
public function activate( $sitewide = false ) {
|
||||
$db = WP_CONTENT_DIR . '/db.php';
|
||||
$create_symlink = defined( 'QM_DB_SYMLINK' ) ? QM_DB_SYMLINK : true;
|
||||
|
||||
if ( $create_symlink && ! file_exists( $db ) && function_exists( 'symlink' ) ) {
|
||||
@symlink( $this->plugin_path( 'wp-content/db.php' ), $db ); // phpcs:ignore
|
||||
}
|
||||
|
||||
if ( $sitewide ) {
|
||||
update_site_option( 'active_sitewide_plugins', get_site_option( 'active_sitewide_plugins' ) );
|
||||
} else {
|
||||
update_option( 'active_plugins', get_option( 'active_plugins' ) );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function deactivate() {
|
||||
$admins = QM_Util::get_admins();
|
||||
|
||||
// Remove legacy capability handling:
|
||||
if ( $admins ) {
|
||||
$admins->remove_cap( 'view_query_monitor' );
|
||||
}
|
||||
|
||||
# Only delete db.php if it belongs to Query Monitor
|
||||
if ( file_exists( WP_CONTENT_DIR . '/db.php' ) && class_exists( 'QM_DB', false ) ) {
|
||||
unlink( WP_CONTENT_DIR . '/db.php' ); // phpcs:ignore
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, string> $plugins
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function filter_active_plugins( $plugins ) {
|
||||
|
||||
// this needs to run on the cli too
|
||||
|
||||
if ( empty( $plugins ) ) {
|
||||
return $plugins;
|
||||
}
|
||||
|
||||
$f = preg_quote( basename( $this->plugin_base() ), '/' );
|
||||
$qm = preg_grep( '/' . $f . '$/', $plugins );
|
||||
$notqm = preg_grep( '/' . $f . '$/', $plugins, PREG_GREP_INVERT );
|
||||
|
||||
if ( false === $qm || false === $notqm ) {
|
||||
return $plugins;
|
||||
}
|
||||
|
||||
return array_merge(
|
||||
$qm,
|
||||
$notqm
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, int> $plugins
|
||||
* @return array<string, int>
|
||||
*/
|
||||
public function filter_active_sitewide_plugins( $plugins ) {
|
||||
|
||||
if ( empty( $plugins ) ) {
|
||||
return $plugins;
|
||||
}
|
||||
|
||||
$f = $this->plugin_base();
|
||||
|
||||
if ( isset( $plugins[ $f ] ) ) {
|
||||
|
||||
unset( $plugins[ $f ] );
|
||||
|
||||
return array_merge( array(
|
||||
$f => time(),
|
||||
), $plugins );
|
||||
|
||||
} else {
|
||||
return $plugins;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
* @return self
|
||||
*/
|
||||
public static function init( $file ) {
|
||||
|
||||
static $instance = null;
|
||||
|
||||
if ( ! $instance ) {
|
||||
$instance = new QM_Activation( $file );
|
||||
}
|
||||
|
||||
return $instance;
|
||||
|
||||
}
|
||||
|
||||
}
|
558
wp-content/plugins/query-monitor/classes/Backtrace.php
Normal file
558
wp-content/plugins/query-monitor/classes/Backtrace.php
Normal file
@ -0,0 +1,558 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Function call backtrace container.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( ! class_exists( 'QM_Backtrace' ) ) {
|
||||
class QM_Backtrace {
|
||||
|
||||
/**
|
||||
* @var array<string, bool>
|
||||
*/
|
||||
protected static $ignore_class = array(
|
||||
'wpdb' => true,
|
||||
'hyperdb' => true,
|
||||
'LudicrousDB' => true,
|
||||
'QueryMonitor' => true,
|
||||
'W3_Db' => true,
|
||||
'Debug_Bar_PHP' => true,
|
||||
'WP_Hook' => true,
|
||||
'Altis\Cloud\DB' => true,
|
||||
'Yoast\WP\Lib\ORM' => true,
|
||||
'Perflab_SQLite_DB' => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array<string, array<string, bool>>
|
||||
*/
|
||||
protected static $ignore_method = array();
|
||||
|
||||
/**
|
||||
* @var array<string, bool>
|
||||
*/
|
||||
protected static $ignore_func = array(
|
||||
'include_once' => true,
|
||||
'require_once' => true,
|
||||
'include' => true,
|
||||
'require' => true,
|
||||
'call_user_func_array' => true,
|
||||
'call_user_func' => true,
|
||||
'trigger_error' => true,
|
||||
'_doing_it_wrong' => true,
|
||||
'_deprecated_argument' => true,
|
||||
'_deprecated_constructor' => true,
|
||||
'_deprecated_file' => true,
|
||||
'_deprecated_function' => true,
|
||||
'_deprecated_hook' => true,
|
||||
'dbDelta' => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array<string, int|string>
|
||||
*/
|
||||
protected static $show_args = array(
|
||||
'do_action' => 1,
|
||||
'apply_filters' => 1,
|
||||
'do_action_ref_array' => 1,
|
||||
'apply_filters_ref_array' => 1,
|
||||
'do_action_deprecated' => 1,
|
||||
'apply_filters_deprecated' => 1,
|
||||
'get_query_template' => 1,
|
||||
'resolve_block_template' => 1,
|
||||
'get_template_part' => 2,
|
||||
'get_extended_template_part' => 2,
|
||||
'ai_get_template_part' => 2,
|
||||
'load_template' => 'dir',
|
||||
'dynamic_sidebar' => 1,
|
||||
'get_header' => 1,
|
||||
'get_sidebar' => 1,
|
||||
'get_footer' => 1,
|
||||
'get_transient' => 1,
|
||||
'set_transient' => 1,
|
||||
'class_exists' => 2,
|
||||
'current_user_can' => 3,
|
||||
'user_can' => 4,
|
||||
'current_user_can_for_blog' => 4,
|
||||
'author_can' => 4,
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array<string, bool>
|
||||
*/
|
||||
protected static $ignore_hook = array();
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected static $filtered = false;
|
||||
|
||||
/**
|
||||
* @var array<string, mixed[]>
|
||||
*/
|
||||
protected $args = array();
|
||||
|
||||
/**
|
||||
* @var mixed[]
|
||||
*/
|
||||
protected $trace;
|
||||
|
||||
/**
|
||||
* @var mixed[]|null
|
||||
*/
|
||||
protected $filtered_trace = null;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $calling_line = 0;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $calling_file = '';
|
||||
|
||||
/**
|
||||
* @var QM_Component|null
|
||||
*/
|
||||
protected $component = null;
|
||||
|
||||
/**
|
||||
* @var mixed[]|null
|
||||
*/
|
||||
protected $top_frame = null;
|
||||
|
||||
/**
|
||||
* @param array<string, mixed[]> $args
|
||||
* @param mixed[] $trace
|
||||
*/
|
||||
public function __construct( array $args = array(), array $trace = null ) {
|
||||
$this->trace = $trace ?? debug_backtrace( 0 );
|
||||
|
||||
$this->args = array_merge( array(
|
||||
'ignore_class' => array(),
|
||||
'ignore_method' => array(),
|
||||
'ignore_func' => array(),
|
||||
'ignore_hook' => array(),
|
||||
'show_args' => array(),
|
||||
), $args );
|
||||
|
||||
foreach ( $this->trace as & $frame ) {
|
||||
if ( ! isset( $frame['args'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( isset( $frame['function'], self::$show_args[ $frame['function'] ] ) ) {
|
||||
$show = self::$show_args[ $frame['function'] ];
|
||||
|
||||
if ( ! is_int( $show ) ) {
|
||||
$show = 1;
|
||||
}
|
||||
|
||||
$frame['args'] = array_slice( $frame['args'], 0, $show );
|
||||
|
||||
} else {
|
||||
unset( $frame['args'] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $frame
|
||||
* @return void
|
||||
*/
|
||||
public function push_frame( array $frame ) {
|
||||
$this->top_frame = $frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_stack() {
|
||||
|
||||
$trace = $this->get_filtered_trace();
|
||||
$stack = array_column( $trace, 'display' );
|
||||
|
||||
return $stack;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]|false
|
||||
*/
|
||||
public function get_caller() {
|
||||
|
||||
$trace = $this->get_filtered_trace();
|
||||
|
||||
return reset( $trace );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return QM_Component
|
||||
*/
|
||||
public function get_component() {
|
||||
if ( isset( $this->component ) ) {
|
||||
return $this->component;
|
||||
}
|
||||
|
||||
$components = array();
|
||||
$frames = $this->get_filtered_trace();
|
||||
|
||||
if ( $this->top_frame ) {
|
||||
array_unshift( $frames, $this->top_frame );
|
||||
}
|
||||
|
||||
foreach ( $frames as $frame ) {
|
||||
$component = self::get_frame_component( $frame );
|
||||
|
||||
if ( $component ) {
|
||||
if ( 'plugin' === $component->type ) {
|
||||
// If the component is a plugin then it can't be anything else,
|
||||
// so short-circuit and return early.
|
||||
$this->component = $component;
|
||||
return $this->component;
|
||||
}
|
||||
|
||||
$components[ $component->type ] = $component;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( QM_Util::get_file_dirs() as $type => $dir ) {
|
||||
if ( isset( $components[ $type ] ) ) {
|
||||
$this->component = $components[ $type ];
|
||||
return $this->component;
|
||||
}
|
||||
}
|
||||
|
||||
$component = new QM_Component();
|
||||
$component->type = 'unknown';
|
||||
$component->name = __( 'Unknown', 'query-monitor' );
|
||||
$component->context = 'unknown';
|
||||
|
||||
return $component;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to determine the component responsible for a given frame.
|
||||
*
|
||||
* @param mixed[] $frame A single frame from a trace.
|
||||
* @phpstan-param array{
|
||||
* class?: class-string,
|
||||
* function?: string,
|
||||
* file?: string,
|
||||
* } $frame
|
||||
* @return QM_Component|null An object representing the component, or null if
|
||||
* the component cannot be determined.
|
||||
*/
|
||||
public static function get_frame_component( array $frame ) {
|
||||
try {
|
||||
|
||||
if ( isset( $frame['class'], $frame['function'] ) ) {
|
||||
if ( ! class_exists( $frame['class'], false ) ) {
|
||||
return null;
|
||||
}
|
||||
if ( ! method_exists( $frame['class'], $frame['function'] ) ) {
|
||||
return null;
|
||||
}
|
||||
$ref = new ReflectionMethod( $frame['class'], $frame['function'] );
|
||||
$file = $ref->getFileName();
|
||||
} elseif ( isset( $frame['function'] ) && function_exists( $frame['function'] ) ) {
|
||||
$ref = new ReflectionFunction( $frame['function'] );
|
||||
$file = $ref->getFileName();
|
||||
} elseif ( isset( $frame['file'] ) ) {
|
||||
$file = $frame['file'];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( ! $file ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return QM_Util::get_file_component( $file );
|
||||
|
||||
} catch ( ReflectionException $e ) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function get_trace() {
|
||||
return $this->trace;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use the `::get_filtered_trace()` method instead.
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function get_display_trace() {
|
||||
return $this->get_filtered_trace();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
* @phpstan-return list<array{
|
||||
* file: string,
|
||||
* line: int,
|
||||
* display: string,
|
||||
* }>
|
||||
*/
|
||||
public function get_filtered_trace() {
|
||||
|
||||
if ( ! isset( $this->filtered_trace ) ) {
|
||||
|
||||
$trace = array_map( array( $this, 'filter_trace' ), $this->trace );
|
||||
$trace = array_values( array_filter( $trace ) );
|
||||
|
||||
if ( empty( $trace ) && ! empty( $this->trace ) ) {
|
||||
$lowest = $this->trace[0];
|
||||
$file = QM_Util::standard_dir( $lowest['file'], '' );
|
||||
$lowest['calling_file'] = $lowest['file'];
|
||||
$lowest['calling_line'] = $lowest['line'];
|
||||
$lowest['function'] = $file;
|
||||
$lowest['display'] = $file;
|
||||
$lowest['id'] = $file;
|
||||
unset( $lowest['class'], $lowest['args'], $lowest['type'] );
|
||||
|
||||
// When a PHP error is triggered which doesn't have a stack trace, for example a
|
||||
// deprecated error, QM will blame itself due to its error handler. This prevents that.
|
||||
if ( false === strpos( $file, 'query-monitor/collectors/php_errors.php' ) ) {
|
||||
$trace[0] = $lowest;
|
||||
}
|
||||
}
|
||||
|
||||
$this->filtered_trace = $trace;
|
||||
|
||||
}
|
||||
|
||||
return $this->filtered_trace;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, string> $stack
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public static function get_filtered_stack( array $stack ) {
|
||||
$trace = new self( array(), array() );
|
||||
$return = array();
|
||||
|
||||
foreach ( $stack as $i => $item ) {
|
||||
$frame = array(
|
||||
'function' => $item,
|
||||
);
|
||||
|
||||
if ( false !== strpos( $item, '->' ) ) {
|
||||
list( $class, $function ) = explode( '->', $item );
|
||||
$frame = array(
|
||||
'class' => $class,
|
||||
'type' => '->',
|
||||
'function' => $function,
|
||||
);
|
||||
}
|
||||
|
||||
if ( false !== strpos( $item, '::' ) ) {
|
||||
list( $class, $function ) = explode( '::', $item );
|
||||
$frame = array(
|
||||
'class' => $class,
|
||||
'type' => '::',
|
||||
'function' => $function,
|
||||
);
|
||||
}
|
||||
|
||||
$frame['args'] = array();
|
||||
|
||||
if ( $trace->filter_trace( $frame ) ) {
|
||||
$return[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use the `ignore_class`, `ignore_method`, `ignore_func`, and `ignore_hook` arguments instead.
|
||||
*
|
||||
* @param int $num
|
||||
* @return self
|
||||
*/
|
||||
public function ignore( $num ) {
|
||||
for ( $i = 0; $i < $num; $i++ ) {
|
||||
unset( $this->trace[ $i ] );
|
||||
}
|
||||
$this->trace = array_values( $this->trace );
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $frame
|
||||
* @return mixed[]|null
|
||||
*/
|
||||
public function filter_trace( array $frame ) {
|
||||
|
||||
if ( ! self::$filtered && function_exists( 'did_action' ) && did_action( 'plugins_loaded' ) ) {
|
||||
|
||||
/**
|
||||
* Filters which classes to ignore when constructing user-facing call stacks.
|
||||
*
|
||||
* @since 2.7.0
|
||||
*
|
||||
* @param array<string, bool> $ignore_class Array of class names to ignore. The array keys are class names to ignore,
|
||||
* the array values are whether to ignore the class (usually true).
|
||||
*/
|
||||
self::$ignore_class = apply_filters( 'qm/trace/ignore_class', self::$ignore_class );
|
||||
|
||||
/**
|
||||
* Filters which class methods to ignore when constructing user-facing call stacks.
|
||||
*
|
||||
* @since 2.7.0
|
||||
*
|
||||
* @param array<string, array<string, bool>> $ignore_method Array of method names to ignore. The top level array keys are
|
||||
* class names, the second level array keys are method names, and
|
||||
* the array values are whether to ignore the method (usually true).
|
||||
*/
|
||||
self::$ignore_method = apply_filters( 'qm/trace/ignore_method', self::$ignore_method );
|
||||
|
||||
/**
|
||||
* Filters which functions to ignore when constructing user-facing call stacks.
|
||||
*
|
||||
* @since 2.7.0
|
||||
*
|
||||
* @param array<string, bool> $ignore_func Array of function names to ignore. The array keys are function names to ignore,
|
||||
* the array values are whether to ignore the function (usually true).
|
||||
*/
|
||||
self::$ignore_func = apply_filters( 'qm/trace/ignore_func', self::$ignore_func );
|
||||
|
||||
/**
|
||||
* Filters which action and filter names to ignore when constructing user-facing call stacks.
|
||||
*
|
||||
* @since 3.8.0
|
||||
*
|
||||
* @param array<string, bool> $ignore_hook Array of hook names to ignore. The array keys are hook names to ignore,
|
||||
* the array values are whether to ignore the hook (usually true).
|
||||
*/
|
||||
self::$ignore_hook = apply_filters( 'qm/trace/ignore_hook', self::$ignore_hook );
|
||||
|
||||
/**
|
||||
* Filters the number of argument values to show for the given function name when constructing user-facing
|
||||
* call stacks.
|
||||
*
|
||||
* @since 2.7.0
|
||||
*
|
||||
* @param array<string,int|string> $show_args The number of argument values to show for the given function name. The
|
||||
* array keys are function names, the array values are either integers or
|
||||
* "dir" to specifically treat the function argument as a directory path.
|
||||
*/
|
||||
self::$show_args = apply_filters( 'qm/trace/show_args', self::$show_args );
|
||||
|
||||
self::$filtered = true;
|
||||
|
||||
}
|
||||
|
||||
$return = $frame;
|
||||
$ignore_class = array_filter( array_merge( self::$ignore_class, $this->args['ignore_class'] ) );
|
||||
$ignore_method = array_filter( array_merge( self::$ignore_method, $this->args['ignore_method'] ) );
|
||||
$ignore_func = array_filter( array_merge( self::$ignore_func, $this->args['ignore_func'] ) );
|
||||
$ignore_hook = array_filter( array_merge( self::$ignore_hook, $this->args['ignore_hook'] ) );
|
||||
$show_args = array_merge( self::$show_args, $this->args['show_args'] );
|
||||
|
||||
$hook_functions = array(
|
||||
'apply_filters' => true,
|
||||
'do_action' => true,
|
||||
'apply_filters_ref_array' => true,
|
||||
'do_action_ref_array' => true,
|
||||
'apply_filters_deprecated' => true,
|
||||
'do_action_deprecated' => true,
|
||||
);
|
||||
|
||||
if ( ! isset( $frame['function'] ) ) {
|
||||
$frame['function'] = '(unknown)';
|
||||
}
|
||||
|
||||
if ( isset( $frame['class'] ) ) {
|
||||
if ( isset( $ignore_class[ $frame['class'] ] ) ) {
|
||||
$return = null;
|
||||
} elseif ( isset( $ignore_method[ $frame['class'] ][ $frame['function'] ] ) ) {
|
||||
$return = null;
|
||||
} elseif ( 0 === strpos( $frame['class'], 'QM' ) ) {
|
||||
$return = null;
|
||||
} else {
|
||||
$return['id'] = $frame['class'] . $frame['type'] . $frame['function'] . '()';
|
||||
$return['display'] = QM_Util::shorten_fqn( $frame['class'] . $frame['type'] . $frame['function'] ) . '()';
|
||||
}
|
||||
} else {
|
||||
if ( isset( $ignore_func[ $frame['function'] ] ) ) {
|
||||
$return = null;
|
||||
} elseif ( isset( $show_args[ $frame['function'] ] ) ) {
|
||||
$show = $show_args[ $frame['function'] ];
|
||||
|
||||
if ( 'dir' === $show ) {
|
||||
if ( isset( $frame['args'][0] ) ) {
|
||||
$arg = QM_Util::standard_dir( $frame['args'][0], '' );
|
||||
$return['id'] = $frame['function'] . '()';
|
||||
$return['display'] = QM_Util::shorten_fqn( $frame['function'] ) . "('{$arg}')";
|
||||
}
|
||||
} else {
|
||||
if ( isset( $hook_functions[ $frame['function'] ], $frame['args'][0] ) && is_string( $frame['args'][0] ) && isset( $ignore_hook[ $frame['args'][0] ] ) ) {
|
||||
$return = null;
|
||||
} else {
|
||||
$args = array();
|
||||
for ( $i = 0; $i < $show; $i++ ) {
|
||||
if ( isset( $frame['args'] ) && array_key_exists( $i, $frame['args'] ) ) {
|
||||
if ( is_string( $frame['args'][ $i ] ) ) {
|
||||
$args[] = '\'' . $frame['args'][ $i ] . '\'';
|
||||
} else {
|
||||
$args[] = QM_Util::display_variable( $frame['args'][ $i ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
$return['id'] = $frame['function'] . '()';
|
||||
$return['display'] = QM_Util::shorten_fqn( $frame['function'] ) . '(' . implode( ',', $args ) . ')';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$return['id'] = $frame['function'] . '()';
|
||||
$return['display'] = QM_Util::shorten_fqn( $frame['function'] ) . '()';
|
||||
}
|
||||
}
|
||||
|
||||
if ( $return ) {
|
||||
|
||||
$return['calling_file'] = $this->calling_file;
|
||||
$return['calling_line'] = $this->calling_line;
|
||||
|
||||
if ( ! isset( $return['file'] ) ) {
|
||||
$return['file'] = $this->calling_file;
|
||||
}
|
||||
|
||||
if ( ! isset( $return['line'] ) ) {
|
||||
$return['line'] = $this->calling_line;
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $frame['line'] ) ) {
|
||||
$this->calling_line = $frame['line'];
|
||||
}
|
||||
if ( isset( $frame['file'] ) ) {
|
||||
$this->calling_file = $frame['file'];
|
||||
}
|
||||
|
||||
return $return;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
|
||||
add_action( 'init', 'QueryMonitor::symlink_warning' );
|
||||
|
||||
}
|
75
wp-content/plugins/query-monitor/classes/CLI.php
Normal file
75
wp-content/plugins/query-monitor/classes/CLI.php
Normal file
@ -0,0 +1,75 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Plugin CLI command.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_CLI extends QM_Plugin {
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
*/
|
||||
protected function __construct( $file ) {
|
||||
|
||||
# Register command
|
||||
WP_CLI::add_command( 'qm enable', array( $this, 'enable' ) );
|
||||
|
||||
# Parent setup:
|
||||
parent::__construct( $file );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable QM by creating the symlink for db.php
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enable() {
|
||||
$drop_in = WP_CONTENT_DIR . '/db.php';
|
||||
|
||||
if ( file_exists( $drop_in ) ) {
|
||||
$contents = file_get_contents( $drop_in );
|
||||
|
||||
if ( false !== $contents && false !== strpos( $contents, 'class QM_DB' ) ) {
|
||||
WP_CLI::success( "Query Monitor's wp-content/db.php is already in place" );
|
||||
exit( 0 );
|
||||
} else {
|
||||
WP_CLI::error( 'Unknown wp-content/db.php is already in place' );
|
||||
}
|
||||
}
|
||||
|
||||
if ( defined( 'QM_DB_SYMLINK' ) && ! QM_DB_SYMLINK ) {
|
||||
WP_CLI::warning( 'Creation of symlink prevented by QM_DB_SYMLINK constant.' );
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
if ( ! function_exists( 'symlink' ) ) {
|
||||
WP_CLI::error( 'The symlink function is not available' );
|
||||
}
|
||||
|
||||
if ( symlink( $this->plugin_path( 'wp-content/db.php' ), $drop_in ) ) {
|
||||
WP_CLI::success( 'wp-content/db.php symlink created' );
|
||||
exit( 0 );
|
||||
} else {
|
||||
WP_CLI::error( 'Failed to create wp-content/db.php symlink' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
* @return self
|
||||
*/
|
||||
public static function init( $file = null ) {
|
||||
|
||||
static $instance = null;
|
||||
|
||||
if ( ! $instance ) {
|
||||
$instance = new QM_CLI( $file );
|
||||
}
|
||||
|
||||
return $instance;
|
||||
|
||||
}
|
||||
|
||||
}
|
389
wp-content/plugins/query-monitor/classes/Collector.php
Normal file
389
wp-content/plugins/query-monitor/classes/Collector.php
Normal file
@ -0,0 +1,389 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Abstract data collector.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! class_exists( 'QM_Collector' ) ) {
|
||||
abstract class QM_Collector {
|
||||
|
||||
/**
|
||||
* @var QM_Timer|null
|
||||
*/
|
||||
protected $timer;
|
||||
|
||||
/**
|
||||
* @var QM_Data
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* @var bool|null
|
||||
*/
|
||||
protected static $hide_qm = null;
|
||||
|
||||
/**
|
||||
* @var array<string, array<string, mixed>>
|
||||
*/
|
||||
public $concerned_actions = array();
|
||||
|
||||
/**
|
||||
* @var array<string, array<string, mixed>>
|
||||
*/
|
||||
public $concerned_filters = array();
|
||||
|
||||
/**
|
||||
* @var array<string, array<string, mixed>>
|
||||
*/
|
||||
public $concerned_constants = array();
|
||||
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
public $tracked_hooks = array();
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $id = '';
|
||||
|
||||
public function __construct() {
|
||||
$this->data = $this->get_storage();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function set_up() {}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
final public function id() {
|
||||
return "qm-{$this->id}";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @return void
|
||||
*/
|
||||
protected function log_type( $type ) {
|
||||
if ( isset( $this->data->types[ $type ] ) ) {
|
||||
$this->data->types[ $type ]++;
|
||||
} else {
|
||||
$this->data->types[ $type ] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param QM_Component $component
|
||||
* @param float $ltime
|
||||
* @param string|int $type
|
||||
* @return void
|
||||
*/
|
||||
protected function log_component( $component, $ltime, $type ) {
|
||||
if ( ! isset( $this->data->component_times[ $component->name ] ) ) {
|
||||
$this->data->component_times[ $component->name ] = array(
|
||||
'component' => $component->name,
|
||||
'ltime' => 0,
|
||||
'types' => array(),
|
||||
);
|
||||
}
|
||||
|
||||
$this->data->component_times[ $component->name ]['ltime'] += $ltime;
|
||||
|
||||
if ( isset( $this->data->component_times[ $component->name ]['types'][ $type ] ) ) {
|
||||
$this->data->component_times[ $component->name ]['types'][ $type ]++;
|
||||
} else {
|
||||
$this->data->component_times[ $component->name ]['types'][ $type ] = 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float
|
||||
*/
|
||||
public static function timer_stop_float() {
|
||||
return microtime( true ) - $_SERVER['REQUEST_TIME_FLOAT'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $constant
|
||||
* @return string
|
||||
*/
|
||||
public static function format_bool_constant( $constant ) {
|
||||
// @TODO this should be in QM_Util
|
||||
|
||||
if ( ! defined( $constant ) ) {
|
||||
/* translators: Undefined PHP constant */
|
||||
return __( 'undefined', 'query-monitor' );
|
||||
} elseif ( is_string( constant( $constant ) ) && ! is_numeric( constant( $constant ) ) ) {
|
||||
return constant( $constant );
|
||||
} elseif ( ! constant( $constant ) ) {
|
||||
return 'false';
|
||||
} else {
|
||||
return 'true';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return QM_Data
|
||||
*/
|
||||
public function get_data() {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return QM_Data
|
||||
*/
|
||||
public function get_storage(): QM_Data {
|
||||
return new QM_Data_Fallback();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
final public function discard_data() {
|
||||
$this->data = $this->get_storage();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
* @return void
|
||||
*/
|
||||
final public function set_id( $id ) {
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
final public function process_concerns() {
|
||||
global $wp_filter;
|
||||
|
||||
$tracked = array();
|
||||
$id = $this->id;
|
||||
|
||||
/**
|
||||
* Filters the concerned actions for the given panel.
|
||||
*
|
||||
* The dynamic portion of the hook name, `$id`, refers to the collector ID, which is typically the `$id`
|
||||
* property of the collector class.
|
||||
*
|
||||
* @since 3.3.0
|
||||
*
|
||||
* @param array<int, string> $actions Array of action names that this panel concerns itself with.
|
||||
*/
|
||||
$concerned_actions = apply_filters( "qm/collect/concerned_actions/{$id}", $this->get_concerned_actions() );
|
||||
|
||||
/**
|
||||
* Filters the concerned filters for the given panel.
|
||||
*
|
||||
* The dynamic portion of the hook name, `$id`, refers to the collector ID, which is typically the `$id`
|
||||
* property of the collector class.
|
||||
*
|
||||
* @since 3.3.0
|
||||
*
|
||||
* @param array<int, string> $filters Array of filter names that this panel concerns itself with.
|
||||
*/
|
||||
$concerned_filters = apply_filters( "qm/collect/concerned_filters/{$id}", $this->get_concerned_filters() );
|
||||
|
||||
/**
|
||||
* Filters the concerned options for the given panel.
|
||||
*
|
||||
* The dynamic portion of the hook name, `$id`, refers to the collector ID, which is typically the `$id`
|
||||
* property of the collector class.
|
||||
*
|
||||
* @since 3.3.0
|
||||
*
|
||||
* @param array<int, string> $options Array of option names that this panel concerns itself with.
|
||||
*/
|
||||
$concerned_options = apply_filters( "qm/collect/concerned_options/{$id}", $this->get_concerned_options() );
|
||||
|
||||
/**
|
||||
* Filters the concerned constants for the given panel.
|
||||
*
|
||||
* The dynamic portion of the hook name, `$id`, refers to the collector ID, which is typically the `$id`
|
||||
* property of the collector class.
|
||||
*
|
||||
* @since 3.3.0
|
||||
*
|
||||
* @param array<int, string> $constants Array of constant names that this panel concerns itself with.
|
||||
*/
|
||||
$concerned_constants = apply_filters( "qm/collect/concerned_constants/{$id}", $this->get_concerned_constants() );
|
||||
|
||||
foreach ( $concerned_actions as $action ) {
|
||||
if ( has_action( $action ) ) {
|
||||
$this->concerned_actions[ $action ] = QM_Hook::process( $action, 'action', $wp_filter, true, false );
|
||||
}
|
||||
$tracked[] = $action;
|
||||
}
|
||||
|
||||
foreach ( $concerned_filters as $filter ) {
|
||||
if ( has_filter( $filter ) ) {
|
||||
$this->concerned_filters[ $filter ] = QM_Hook::process( $filter, 'filter', $wp_filter, true, false );
|
||||
}
|
||||
$tracked[] = $filter;
|
||||
}
|
||||
|
||||
$option_filters = array(
|
||||
// Should this include the pre_delete_ and pre_update_ filters too?
|
||||
'pre_option_%s',
|
||||
'default_option_%s',
|
||||
'option_%s',
|
||||
'pre_site_option_%s',
|
||||
'default_site_option_%s',
|
||||
'site_option_%s',
|
||||
);
|
||||
|
||||
foreach ( $concerned_options as $option ) {
|
||||
foreach ( $option_filters as $option_filter ) {
|
||||
$filter = sprintf(
|
||||
$option_filter,
|
||||
$option
|
||||
);
|
||||
if ( has_filter( $filter ) ) {
|
||||
$this->concerned_filters[ $filter ] = QM_Hook::process( $filter, 'filter', $wp_filter, true, false );
|
||||
}
|
||||
$tracked[] = $filter;
|
||||
}
|
||||
}
|
||||
|
||||
$this->concerned_actions = array_filter( $this->concerned_actions, array( $this, 'filter_concerns' ) );
|
||||
$this->concerned_filters = array_filter( $this->concerned_filters, array( $this, 'filter_concerns' ) );
|
||||
|
||||
foreach ( $concerned_constants as $constant ) {
|
||||
if ( defined( $constant ) ) {
|
||||
$this->concerned_constants[ $constant ] = constant( $constant );
|
||||
}
|
||||
}
|
||||
|
||||
sort( $tracked );
|
||||
|
||||
$this->tracked_hooks = $tracked;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $concerns
|
||||
* @return bool
|
||||
*/
|
||||
public function filter_concerns( $concerns ) {
|
||||
return ! empty( $concerns['actions'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WP_User $user_object
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public static function format_user( WP_User $user_object ) {
|
||||
$user = get_object_vars( $user_object->data );
|
||||
unset(
|
||||
$user['user_pass'],
|
||||
$user['user_activation_key']
|
||||
);
|
||||
$user['roles'] = $user_object->roles;
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function enabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function hide_qm() {
|
||||
if ( ! defined( 'QM_HIDE_SELF' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( null === self::$hide_qm ) {
|
||||
self::$hide_qm = QM_HIDE_SELF;
|
||||
}
|
||||
|
||||
return self::$hide_qm;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $item
|
||||
* @phpstan-param array{
|
||||
* component: QM_Component,
|
||||
* } $item
|
||||
* @return bool
|
||||
*/
|
||||
public function filter_remove_qm( array $item ) {
|
||||
return ( 'query-monitor' !== $item['component']->context );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $items
|
||||
* @return bool
|
||||
*/
|
||||
public function filter_dupe_items( $items ) {
|
||||
return ( count( $items ) > 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function process() {}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function post_process() {}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function tear_down() {}
|
||||
|
||||
/**
|
||||
* @return QM_Timer|null
|
||||
*/
|
||||
public function get_timer() {
|
||||
return $this->timer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param QM_Timer $timer
|
||||
* @return void
|
||||
*/
|
||||
public function set_timer( QM_Timer $timer ) {
|
||||
$this->timer = $timer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_concerned_actions() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_concerned_filters() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_concerned_options() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_concerned_constants() {
|
||||
return array();
|
||||
}
|
||||
}
|
||||
}
|
340
wp-content/plugins/query-monitor/classes/Collector_Assets.php
Normal file
340
wp-content/plugins/query-monitor/classes/Collector_Assets.php
Normal file
@ -0,0 +1,340 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Enqueued scripts and styles collector.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @extends QM_DataCollector<QM_Data_Assets>
|
||||
*/
|
||||
abstract class QM_Collector_Assets extends QM_DataCollector {
|
||||
|
||||
public function get_storage(): QM_Data {
|
||||
return new QM_Data_Assets();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function set_up() {
|
||||
parent::set_up();
|
||||
add_action( 'admin_print_footer_scripts', array( $this, 'action_print_footer_scripts' ), 9999 );
|
||||
add_action( 'wp_print_footer_scripts', array( $this, 'action_print_footer_scripts' ), 9999 );
|
||||
add_action( 'admin_head', array( $this, 'action_head' ), 9999 );
|
||||
add_action( 'wp_head', array( $this, 'action_head' ), 9999 );
|
||||
add_action( 'login_head', array( $this, 'action_head' ), 9999 );
|
||||
add_action( 'embed_head', array( $this, 'action_head' ), 9999 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function tear_down() {
|
||||
remove_action( 'admin_print_footer_scripts', array( $this, 'action_print_footer_scripts' ), 9999 );
|
||||
remove_action( 'wp_print_footer_scripts', array( $this, 'action_print_footer_scripts' ), 9999 );
|
||||
remove_action( 'admin_head', array( $this, 'action_head' ), 9999 );
|
||||
remove_action( 'wp_head', array( $this, 'action_head' ), 9999 );
|
||||
remove_action( 'login_head', array( $this, 'action_head' ), 9999 );
|
||||
remove_action( 'embed_head', array( $this, 'action_head' ), 9999 );
|
||||
|
||||
parent::tear_down();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
abstract public function get_dependency_type();
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function action_head() {
|
||||
$type = $this->get_dependency_type();
|
||||
|
||||
/** @var WP_Dependencies $dependencies */
|
||||
$dependencies = $GLOBALS[ "wp_{$type}" ];
|
||||
|
||||
$this->data->header = $dependencies->done;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function action_print_footer_scripts() {
|
||||
if ( empty( $this->data->header ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$type = $this->get_dependency_type();
|
||||
|
||||
/** @var WP_Dependencies $dependencies */
|
||||
$dependencies = $GLOBALS[ "wp_{$type}" ];
|
||||
|
||||
$this->data->footer = array_diff( $dependencies->done, $this->data->header );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function process() {
|
||||
if ( empty( $this->data->header ) && empty( $this->data->footer ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->data->is_ssl = is_ssl();
|
||||
$this->data->host = wp_unslash( $_SERVER['HTTP_HOST'] );
|
||||
$this->data->default_version = get_bloginfo( 'version' );
|
||||
$this->data->port = (string) parse_url( $this->data->host, PHP_URL_PORT );
|
||||
|
||||
$positions = array(
|
||||
'missing',
|
||||
'broken',
|
||||
'header',
|
||||
'footer',
|
||||
);
|
||||
|
||||
$this->data->counts = array(
|
||||
'missing' => 0,
|
||||
'broken' => 0,
|
||||
'header' => 0,
|
||||
'footer' => 0,
|
||||
'total' => 0,
|
||||
);
|
||||
|
||||
$type = $this->get_dependency_type();
|
||||
|
||||
foreach ( array( 'header', 'footer' ) as $position ) {
|
||||
if ( empty( $this->data->{$position} ) ) {
|
||||
$this->data->{$position} = array();
|
||||
}
|
||||
}
|
||||
|
||||
/** @var WP_Dependencies $raw */
|
||||
$raw = $GLOBALS[ "wp_{$type}" ];
|
||||
$broken = array_values( array_diff( $raw->queue, $raw->done ) );
|
||||
$missing = array_values( array_diff( $raw->queue, array_keys( $raw->registered ) ) );
|
||||
|
||||
// A broken asset is one which has been deregistered without also being dequeued
|
||||
if ( ! empty( $broken ) ) {
|
||||
foreach ( $broken as $key => $handle ) {
|
||||
/** @var _WP_Dependency|false $item */
|
||||
$item = $raw->query( $handle );
|
||||
if ( $item ) {
|
||||
$broken = array_merge( $broken, self::get_broken_dependencies( $item, $raw ) );
|
||||
} else {
|
||||
unset( $broken[ $key ] );
|
||||
$missing[] = $handle;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $broken ) ) {
|
||||
$this->data->broken = array_unique( $broken );
|
||||
}
|
||||
}
|
||||
|
||||
// A missing asset is one which has been enqueued with dependencies that don't exist
|
||||
if ( ! empty( $missing ) ) {
|
||||
$this->data->missing = array_unique( $missing );
|
||||
foreach ( $this->data->missing as $handle ) {
|
||||
$raw->add( $handle, false );
|
||||
$key = array_search( $handle, $raw->done, true );
|
||||
if ( false !== $key ) {
|
||||
unset( $raw->done[ $key ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$all_dependencies = array();
|
||||
$all_dependents = array();
|
||||
|
||||
$missing_dependencies = array();
|
||||
|
||||
foreach ( $positions as $position ) {
|
||||
if ( empty( $this->data->{$position} ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @var string $handle */
|
||||
foreach ( $this->data->{$position} as $handle ) {
|
||||
/** @var _WP_Dependency|false $dependency */
|
||||
$dependency = $raw->query( $handle );
|
||||
|
||||
if ( ! $dependency ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$all_dependencies = array_merge( $all_dependencies, $dependency->deps );
|
||||
$dependents = $this->get_dependents( $dependency, $raw );
|
||||
$all_dependents = array_merge( $all_dependents, $dependents );
|
||||
|
||||
list( $host, $source, $local, $port ) = $this->get_dependency_data( $dependency );
|
||||
|
||||
if ( empty( $dependency->ver ) ) {
|
||||
$ver = '';
|
||||
} else {
|
||||
$ver = $dependency->ver;
|
||||
}
|
||||
|
||||
$warning = ! in_array( $handle, $raw->done, true );
|
||||
|
||||
if ( $source instanceof WP_Error ) {
|
||||
$display = $source->get_error_message();
|
||||
} else {
|
||||
$display = ltrim( preg_replace( '#https?://' . preg_quote( $this->data->host, '#' ) . '#', '', remove_query_arg( 'ver', $source ) ), '/' );
|
||||
}
|
||||
|
||||
$dependencies = $dependency->deps;
|
||||
|
||||
foreach ( $dependencies as $dep ) {
|
||||
if ( ! $raw->query( $dep ) ) {
|
||||
// A missing dependency is a dependency on an asset that doesn't exist
|
||||
$missing_dependencies[ $dep ] = true;
|
||||
}
|
||||
}
|
||||
|
||||
$this->data->assets[ $position ][ $handle ] = array(
|
||||
'host' => $host,
|
||||
'port' => $port,
|
||||
'source' => $source,
|
||||
'local' => $local,
|
||||
'ver' => $ver,
|
||||
'warning' => $warning,
|
||||
'display' => $display,
|
||||
'dependents' => $dependents,
|
||||
'dependencies' => $dependencies,
|
||||
);
|
||||
|
||||
$this->data->counts[ $position ]++;
|
||||
$this->data->counts['total']++;
|
||||
}
|
||||
}
|
||||
|
||||
unset( $this->data->{$position} );
|
||||
|
||||
$all_dependencies = array_unique( $all_dependencies );
|
||||
sort( $all_dependencies );
|
||||
$this->data->dependencies = $all_dependencies;
|
||||
|
||||
$all_dependents = array_unique( $all_dependents );
|
||||
sort( $all_dependents );
|
||||
$this->data->dependents = $all_dependents;
|
||||
|
||||
$this->data->missing_dependencies = $missing_dependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param _WP_Dependency $item
|
||||
* @param WP_Dependencies $dependencies
|
||||
* @return array<int, string>
|
||||
*/
|
||||
protected static function get_broken_dependencies( _WP_Dependency $item, WP_Dependencies $dependencies ) {
|
||||
$broken = array();
|
||||
|
||||
foreach ( $item->deps as $handle ) {
|
||||
$dep = $dependencies->query( $handle );
|
||||
if ( $dep instanceof _WP_Dependency ) {
|
||||
$broken = array_merge( $broken, self::get_broken_dependencies( $dep, $dependencies ) );
|
||||
} else {
|
||||
$broken[] = $item->handle;
|
||||
}
|
||||
}
|
||||
|
||||
return $broken;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param _WP_Dependency $dependency
|
||||
* @param WP_Dependencies $dependencies
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_dependents( _WP_Dependency $dependency, WP_Dependencies $dependencies ) {
|
||||
$dependents = array();
|
||||
$handles = array_unique( array_merge( $dependencies->queue, $dependencies->done ) );
|
||||
|
||||
foreach ( $handles as $handle ) {
|
||||
$item = $dependencies->query( $handle );
|
||||
if ( $item instanceof _WP_Dependency ) {
|
||||
if ( in_array( $dependency->handle, $item->deps, true ) ) {
|
||||
$dependents[] = $handle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort( $dependents );
|
||||
|
||||
return $dependents;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param _WP_Dependency $dependency
|
||||
* @return mixed[]
|
||||
* @phpstan-return array{
|
||||
* 0: string,
|
||||
* 1: string|WP_Error,
|
||||
* 2: bool,
|
||||
* 3: string,
|
||||
* }
|
||||
*/
|
||||
public function get_dependency_data( _WP_Dependency $dependency ) {
|
||||
/** @var QM_Data_Assets */
|
||||
$data = $this->get_data();
|
||||
$loader = rtrim( $this->get_dependency_type(), 's' );
|
||||
$src = $dependency->src;
|
||||
$host = '';
|
||||
$scheme = '';
|
||||
$port = '';
|
||||
|
||||
if ( null === $dependency->ver ) {
|
||||
$ver = '';
|
||||
} else {
|
||||
$ver = $dependency->ver ?: $this->data->default_version;
|
||||
}
|
||||
|
||||
if ( ! empty( $src ) && ! empty( $ver ) ) {
|
||||
$src = add_query_arg( 'ver', $ver, $src );
|
||||
}
|
||||
|
||||
/** This filter is documented in wp-includes/class.wp-scripts.php */
|
||||
$source = apply_filters( "{$loader}_loader_src", $src, $dependency->handle );
|
||||
|
||||
if ( is_string( $source ) ) {
|
||||
$host = (string) parse_url( $source, PHP_URL_HOST );
|
||||
$scheme = (string) parse_url( $source, PHP_URL_SCHEME );
|
||||
$port = (string) parse_url( $source, PHP_URL_PORT );
|
||||
}
|
||||
|
||||
$http_host = $data->host;
|
||||
$http_port = $data->port;
|
||||
|
||||
if ( empty( $host ) && ! empty( $http_host ) ) {
|
||||
$host = $http_host;
|
||||
$port = $http_port;
|
||||
}
|
||||
|
||||
if ( $scheme && $data->is_ssl && ( 'https' !== $scheme ) && ( 'localhost' !== $host ) ) {
|
||||
$source = new WP_Error( 'qm_insecure_content', __( 'Insecure content', 'query-monitor' ), array(
|
||||
'src' => $source,
|
||||
) );
|
||||
}
|
||||
|
||||
if ( $source instanceof WP_Error ) {
|
||||
$error_data = $source->get_error_data();
|
||||
if ( $error_data && isset( $error_data['src'] ) ) {
|
||||
$host = (string) parse_url( $error_data['src'], PHP_URL_HOST );
|
||||
}
|
||||
} elseif ( empty( $source ) ) {
|
||||
$source = '';
|
||||
$host = '';
|
||||
}
|
||||
|
||||
$local = ( $http_host === $host );
|
||||
|
||||
return array( $host, $source, $local, $port );
|
||||
}
|
||||
|
||||
}
|
112
wp-content/plugins/query-monitor/classes/Collectors.php
Normal file
112
wp-content/plugins/query-monitor/classes/Collectors.php
Normal file
@ -0,0 +1,112 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Container for data collectors.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! class_exists( 'QM_Collectors' ) ) {
|
||||
/**
|
||||
* @implements \IteratorAggregate<string, QM_Collector>
|
||||
*/
|
||||
class QM_Collectors implements IteratorAggregate {
|
||||
|
||||
/**
|
||||
* @var array<string, QM_Collector>
|
||||
*/
|
||||
private $items = array();
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
private $processed = false;
|
||||
|
||||
/**
|
||||
* @return ArrayIterator<string, QM_Collector>
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator() {
|
||||
return new ArrayIterator( $this->items );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param QM_Collector $collector
|
||||
* @return void
|
||||
*/
|
||||
public static function add( QM_Collector $collector ) {
|
||||
$collectors = self::init();
|
||||
|
||||
$collector->set_up();
|
||||
|
||||
$collectors->items[ $collector->id ] = $collector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a collector instance.
|
||||
*
|
||||
* @param string $id The collector ID.
|
||||
* @return QM_Collector|null The collector object.
|
||||
*/
|
||||
public static function get( $id ) {
|
||||
$collectors = self::init();
|
||||
|
||||
return $collectors->items[ $id ] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self
|
||||
*/
|
||||
public static function init() {
|
||||
static $instance;
|
||||
|
||||
if ( ! $instance ) {
|
||||
$instance = new QM_Collectors();
|
||||
}
|
||||
|
||||
return $instance;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function process() {
|
||||
if ( $this->processed ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $this as $collector ) {
|
||||
$collector->tear_down();
|
||||
|
||||
$timer = new QM_Timer();
|
||||
$timer->start();
|
||||
|
||||
$collector->process();
|
||||
$collector->process_concerns();
|
||||
|
||||
$collector->set_timer( $timer->stop() );
|
||||
}
|
||||
|
||||
foreach ( $this as $collector ) {
|
||||
$collector->post_process();
|
||||
}
|
||||
|
||||
$this->processed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function cease() {
|
||||
$collectors = self::init();
|
||||
|
||||
$collectors->processed = true;
|
||||
|
||||
/** @var QM_Collector $collector */
|
||||
foreach ( $collectors as $collector ) {
|
||||
$collector->tear_down();
|
||||
$collector->discard_data();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
23
wp-content/plugins/query-monitor/classes/Component.php
Normal file
23
wp-content/plugins/query-monitor/classes/Component.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Class representing a component.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_Component {
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $context;
|
||||
}
|
64
wp-content/plugins/query-monitor/classes/DB.php
Normal file
64
wp-content/plugins/query-monitor/classes/DB.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Database class used by the database dropin.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_DB extends wpdb {
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
public $time_start;
|
||||
|
||||
/**
|
||||
* Performs a MySQL database query, using current database connection.
|
||||
*
|
||||
* @see wpdb::query()
|
||||
*
|
||||
* @param string $query Database query
|
||||
* @return int|bool Boolean true for CREATE, ALTER, TRUNCATE and DROP queries. Number of rows
|
||||
* affected/selected for all other queries. Boolean false on error.
|
||||
*/
|
||||
public function query( $query ) {
|
||||
if ( $this->show_errors ) {
|
||||
$this->hide_errors();
|
||||
}
|
||||
|
||||
$result = parent::query( $query );
|
||||
$i = $this->num_queries - 1;
|
||||
|
||||
if ( did_action( 'qm/cease' ) ) {
|
||||
// It's not possible to prevent the parent class from logging queries because it reads
|
||||
// the `SAVEQUERIES` constant and I don't want to override more methods than necessary.
|
||||
$this->queries = array();
|
||||
}
|
||||
|
||||
if ( ! isset( $this->queries[ $i ] ) ) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$this->queries[ $i ]['trace'] = new QM_Backtrace();
|
||||
|
||||
if ( ! isset( $this->queries[ $i ][3] ) ) {
|
||||
$this->queries[ $i ][3] = $this->time_start;
|
||||
}
|
||||
|
||||
if ( $this->last_error ) {
|
||||
$code = 'qmdb';
|
||||
|
||||
// This needs to remain in place to account for a user still on PHP 5. Don't want to kill their site.
|
||||
if ( $this->dbh instanceof mysqli ) {
|
||||
$code = mysqli_errno( $this->dbh );
|
||||
}
|
||||
|
||||
$this->queries[ $i ]['result'] = new WP_Error( $code, $this->last_error );
|
||||
} else {
|
||||
$this->queries[ $i ]['result'] = $result;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
67
wp-content/plugins/query-monitor/classes/Data.php
Normal file
67
wp-content/plugins/query-monitor/classes/Data.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Abstract data transfer object.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @implements ArrayAccess<string,mixed>
|
||||
*/
|
||||
#[AllowDynamicProperties]
|
||||
abstract class QM_Data implements \ArrayAccess {
|
||||
/**
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
public $types = array();
|
||||
|
||||
/**
|
||||
* @var array<string, array<string, mixed>>
|
||||
* @phpstan-var array<string, array{
|
||||
* component: string,
|
||||
* ltime: float,
|
||||
* types: array<array-key, int>,
|
||||
* }>
|
||||
*/
|
||||
public $component_times = array();
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
final public function offsetSet( $offset, $value ) {
|
||||
if ( is_string( $offset ) ) {
|
||||
$this->$offset = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
* @return bool
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
final public function offsetExists( $offset ) {
|
||||
return is_string( $offset ) && isset( $this->$offset );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
* @return void
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
final public function offsetUnset( $offset ) {
|
||||
// @TODO might be able to no-op this
|
||||
if ( is_string( $offset ) ) {
|
||||
unset( $this->$offset );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
* @return mixed
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
final public function offsetGet( $offset ) {
|
||||
return ( is_string( $offset ) && isset( $this->$offset ) ) ? $this->$offset : null;
|
||||
}
|
||||
}
|
25
wp-content/plugins/query-monitor/classes/DataCollector.php
Normal file
25
wp-content/plugins/query-monitor/classes/DataCollector.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Abstract data collector for structured data.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
/**
|
||||
* @phpstan-template T of QM_Data
|
||||
*/
|
||||
abstract class QM_DataCollector extends QM_Collector {
|
||||
/**
|
||||
* @var QM_Data
|
||||
* @phpstan-var T
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* @return QM_Data
|
||||
* @phpstan-return T
|
||||
*/
|
||||
final public function get_data() {
|
||||
return $this->data;
|
||||
}
|
||||
}
|
239
wp-content/plugins/query-monitor/classes/Dispatcher.php
Normal file
239
wp-content/plugins/query-monitor/classes/Dispatcher.php
Normal file
@ -0,0 +1,239 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Abstract dispatcher.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! class_exists( 'QM_Dispatcher' ) ) {
|
||||
abstract class QM_Dispatcher {
|
||||
|
||||
/**
|
||||
* Outputter instances.
|
||||
*
|
||||
* @var array<string, QM_Output> Array of outputters.
|
||||
*/
|
||||
protected $outputters = array();
|
||||
|
||||
/**
|
||||
* Query Monitor plugin instance.
|
||||
*
|
||||
* @var QM_Plugin Plugin instance.
|
||||
*/
|
||||
protected $qm;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $id = '';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $ceased = false;
|
||||
|
||||
public function __construct( QM_Plugin $qm ) {
|
||||
$this->qm = $qm;
|
||||
|
||||
if ( ! defined( 'QM_COOKIE' ) ) {
|
||||
define( 'QM_COOKIE', 'wp-query_monitor_' . COOKIEHASH );
|
||||
}
|
||||
if ( ! defined( 'QM_EDITOR_COOKIE' ) ) {
|
||||
define( 'QM_EDITOR_COOKIE', 'wp-query_monitor_editor_' . COOKIEHASH );
|
||||
}
|
||||
|
||||
add_action( 'init', array( $this, 'init' ) );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function is_active();
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
final public function should_dispatch() {
|
||||
|
||||
$e = error_get_last();
|
||||
|
||||
# Don't dispatch if a fatal has occurred:
|
||||
if ( ! empty( $e ) && ( $e['type'] & QM_ERROR_FATALS ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows users to disable this dispatcher.
|
||||
*
|
||||
* The dynamic portion of the hook name, `$this->id`, refers to the dispatcher ID.
|
||||
*
|
||||
* Possible filter names include:
|
||||
*
|
||||
* - `qm/dispatch/html`
|
||||
* - `qm/dispatch/ajax`
|
||||
* - `qm/dispatch/redirect`
|
||||
* - `qm/dispatch/rest`
|
||||
* - `qm/dispatch/wp_die`
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param bool $true Whether or not the dispatcher is enabled.
|
||||
*/
|
||||
if ( ! apply_filters( "qm/dispatch/{$this->id}", true ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->is_active();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function cease() {
|
||||
$this->ceased = true;
|
||||
|
||||
add_filter( "qm/dispatch/{$this->id}", '__return_false' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes and fetches the outputters for this dispatcher.
|
||||
*
|
||||
* @param string $outputter_id The outputter ID.
|
||||
* @return array<string, QM_Output> Array of outputters.
|
||||
*/
|
||||
public function get_outputters( $outputter_id ) {
|
||||
$collectors = QM_Collectors::init();
|
||||
$collectors->process();
|
||||
|
||||
/**
|
||||
* Allows users to filter what outputs.
|
||||
*
|
||||
* The dynamic portion of the hook name, `$outputter_id`, refers to the outputter ID.
|
||||
*
|
||||
* @since 2.8.0
|
||||
*
|
||||
* @param array<string, QM_Output> $outputters Array of outputters.
|
||||
* @param QM_Collectors $collectors List of collectors.
|
||||
*/
|
||||
$this->outputters = apply_filters( "qm/outputter/{$outputter_id}", array(), $collectors );
|
||||
|
||||
return $this->outputters;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
if ( ! self::user_can_view() ) {
|
||||
do_action( 'qm/cease' );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! defined( 'DONOTCACHEPAGE' ) ) {
|
||||
define( 'DONOTCACHEPAGE', 1 );
|
||||
}
|
||||
|
||||
add_action( 'send_headers', 'nocache_headers' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function before_output() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function after_output() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function user_can_view() {
|
||||
|
||||
if ( ! did_action( 'plugins_loaded' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( current_user_can( 'view_query_monitor' ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return self::user_verified();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function user_verified() {
|
||||
if ( isset( $_COOKIE[QM_COOKIE] ) ) { // phpcs:ignore
|
||||
return self::verify_cookie( wp_unslash( $_COOKIE[QM_COOKIE] ) ); // phpcs:ignore
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public static function editor_cookie() {
|
||||
if ( defined( 'QM_EDITOR_COOKIE' ) && isset( $_COOKIE[QM_EDITOR_COOKIE] ) ) { // phpcs:ignore
|
||||
return $_COOKIE[QM_EDITOR_COOKIE]; // phpcs:ignore
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @return bool
|
||||
*/
|
||||
public static function verify_cookie( $value ) {
|
||||
$old_user_id = wp_validate_auth_cookie( $value, 'logged_in' );
|
||||
if ( $old_user_id ) {
|
||||
return user_can( $old_user_id, 'view_query_monitor' );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to switch to the given locale.
|
||||
*
|
||||
* This is a wrapper around `switch_to_locale()` which is safe to call at any point, even
|
||||
* before the `$wp_locale_switcher` global is initialised or if the function does not exist.
|
||||
*
|
||||
* @param string $locale The locale.
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public static function switch_to_locale( $locale ) {
|
||||
global $wp_locale_switcher;
|
||||
|
||||
if ( function_exists( 'switch_to_locale' ) && ( $wp_locale_switcher instanceof WP_Locale_Switcher ) ) {
|
||||
return switch_to_locale( $locale );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to restore the previous locale.
|
||||
*
|
||||
* This is a wrapper around `restore_previous_locale()` which is safe to call at any point, even
|
||||
* before the `$wp_locale_switcher` global is initialised or if the function does not exist.
|
||||
*
|
||||
* @return string|false Locale on success, false on error.
|
||||
*/
|
||||
public static function restore_previous_locale() {
|
||||
global $wp_locale_switcher;
|
||||
|
||||
if ( function_exists( 'restore_previous_locale' ) && ( $wp_locale_switcher instanceof WP_Locale_Switcher ) ) {
|
||||
return restore_previous_locale();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
71
wp-content/plugins/query-monitor/classes/Dispatchers.php
Normal file
71
wp-content/plugins/query-monitor/classes/Dispatchers.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Container for dispatchers.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
/**
|
||||
* @implements \IteratorAggregate<string, QM_Dispatcher>
|
||||
*/
|
||||
class QM_Dispatchers implements IteratorAggregate {
|
||||
|
||||
/**
|
||||
* @var array<string, QM_Dispatcher>
|
||||
*/
|
||||
private $items = array();
|
||||
|
||||
/**
|
||||
* @return ArrayIterator<string, QM_Dispatcher>
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator() {
|
||||
return new ArrayIterator( $this->items );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param QM_Dispatcher $dispatcher
|
||||
* @return void
|
||||
*/
|
||||
public static function add( QM_Dispatcher $dispatcher ) {
|
||||
$dispatchers = self::init();
|
||||
$dispatchers->items[ $dispatcher->id ] = $dispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
* @return QM_Dispatcher|false
|
||||
*/
|
||||
public static function get( $id ) {
|
||||
$dispatchers = self::init();
|
||||
|
||||
return $dispatchers->items[ $id ] ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function cease() {
|
||||
$dispatchers = self::init();
|
||||
|
||||
/** @var QM_Dispatcher $dispatcher */
|
||||
foreach ( $dispatchers as $dispatcher ) {
|
||||
$dispatcher->cease();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self
|
||||
*/
|
||||
public static function init() {
|
||||
static $instance;
|
||||
|
||||
if ( ! $instance ) {
|
||||
$instance = new QM_Dispatchers();
|
||||
}
|
||||
|
||||
return $instance;
|
||||
|
||||
}
|
||||
|
||||
}
|
77
wp-content/plugins/query-monitor/classes/Hook.php
Normal file
77
wp-content/plugins/query-monitor/classes/Hook.php
Normal file
@ -0,0 +1,77 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Hook processor.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_Hook {
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param string $type
|
||||
* @param array<string, WP_Hook> $wp_filter
|
||||
* @param bool $hide_qm
|
||||
* @param bool $hide_core
|
||||
* @return array<int, array<string, mixed>>
|
||||
* @phpstan-param 'action'|'filter' $type
|
||||
* @phpstan-return array{
|
||||
* name: string,
|
||||
* type: 'action'|'filter',
|
||||
* actions: list<array{
|
||||
* priority: int,
|
||||
* callback: array<string, mixed>,
|
||||
* }>,
|
||||
* parts: list<string>,
|
||||
* components: array<string, string>,
|
||||
* }
|
||||
*/
|
||||
public static function process( $name, string $type, array $wp_filter, $hide_qm = false, $hide_core = false ) {
|
||||
|
||||
$actions = array();
|
||||
$components = array();
|
||||
|
||||
if ( isset( $wp_filter[ $name ] ) ) {
|
||||
|
||||
# http://core.trac.wordpress.org/ticket/17817
|
||||
$action = $wp_filter[ $name ];
|
||||
|
||||
foreach ( $action as $priority => $callbacks ) {
|
||||
|
||||
foreach ( $callbacks as $cb ) {
|
||||
|
||||
$callback = QM_Util::populate_callback( $cb );
|
||||
|
||||
if ( isset( $callback['component'] ) ) {
|
||||
if (
|
||||
( $hide_qm && 'query-monitor' === $callback['component']->context )
|
||||
|| ( $hide_core && 'core' === $callback['component']->context )
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$components[ $callback['component']->name ] = $callback['component']->name;
|
||||
}
|
||||
|
||||
$actions[] = array(
|
||||
'priority' => $priority,
|
||||
'callback' => $callback,
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$parts = array_values( array_filter( (array) preg_split( '#[_/.-]#', $name ) ) );
|
||||
|
||||
return array(
|
||||
'name' => $name,
|
||||
'type' => $type,
|
||||
'actions' => $actions,
|
||||
'parts' => $parts,
|
||||
'components' => $components,
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
64
wp-content/plugins/query-monitor/classes/Output.php
Normal file
64
wp-content/plugins/query-monitor/classes/Output.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Abstract output handler.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! class_exists( 'QM_Output' ) ) {
|
||||
abstract class QM_Output {
|
||||
|
||||
/**
|
||||
* Collector instance.
|
||||
*
|
||||
* @var QM_Collector Collector.
|
||||
*/
|
||||
protected $collector;
|
||||
|
||||
/**
|
||||
* Timer instance.
|
||||
*
|
||||
* @var QM_Timer|null Timer.
|
||||
*/
|
||||
protected $timer;
|
||||
|
||||
public function __construct( QM_Collector $collector ) {
|
||||
$this->collector = $collector;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function get_output();
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function output() {
|
||||
// nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* @return QM_Collector
|
||||
*/
|
||||
public function get_collector() {
|
||||
return $this->collector;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return QM_Timer|null
|
||||
*/
|
||||
final public function get_timer() {
|
||||
return $this->timer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param QM_Timer $timer
|
||||
* @return void
|
||||
*/
|
||||
final public function set_timer( QM_Timer $timer ) {
|
||||
$this->timer = $timer;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
65
wp-content/plugins/query-monitor/classes/PHP.php
Normal file
65
wp-content/plugins/query-monitor/classes/PHP.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/**
|
||||
* PHP version compatibility functionality.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! class_exists( 'QM_PHP' ) ) {
|
||||
class QM_PHP {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public static $minimum_version = '7.4.0';
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function version_met() {
|
||||
return version_compare( PHP_VERSION, self::$minimum_version, '>=' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function php_version_nope() {
|
||||
printf(
|
||||
'<div id="qm-php-nope" class="notice notice-error is-dismissible"><p>%s</p></div>',
|
||||
wp_kses(
|
||||
sprintf(
|
||||
/* translators: 1: Required PHP version number, 2: Current PHP version number, 3: URL of PHP update help page */
|
||||
__( 'The Query Monitor plugin requires PHP version %1$s or higher. This site is running PHP version %2$s. <a href="%3$s">Learn about updating PHP</a>.', 'query-monitor' ),
|
||||
self::$minimum_version,
|
||||
PHP_VERSION,
|
||||
'https://wordpress.org/support/update-php/'
|
||||
),
|
||||
array(
|
||||
'a' => array(
|
||||
'href' => array(),
|
||||
),
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function vendor_nope() {
|
||||
printf(
|
||||
'<div id="qm-built-nope" class="notice notice-error"><p>%s</p></div>',
|
||||
sprintf(
|
||||
/* translators: 1: CLI command to run, 2: plugin directory name */
|
||||
esc_html__( 'Dependencies for Query Monitor need to be installed. Run %1$s from the %2$s directory.', 'query-monitor' ),
|
||||
'<code>composer install --no-dev</code>',
|
||||
sprintf(
|
||||
'<code>%s</code>',
|
||||
esc_html( dirname( dirname( __FILE__ ) ) )
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
109
wp-content/plugins/query-monitor/classes/Plugin.php
Normal file
109
wp-content/plugins/query-monitor/classes/Plugin.php
Normal file
@ -0,0 +1,109 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Abstract plugin wrapper.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! class_exists( 'QM_Plugin' ) ) {
|
||||
abstract class QM_Plugin {
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
private $plugin = array();
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $file = '';
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param string $file
|
||||
*/
|
||||
protected function __construct( $file ) {
|
||||
$this->file = $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL for for a file/dir within this plugin.
|
||||
*
|
||||
* @param string $file The path within this plugin, e.g. '/js/clever-fx.js'
|
||||
* @return string URL
|
||||
*/
|
||||
final public function plugin_url( $file = '' ) {
|
||||
return $this->_plugin( 'url', $file );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the filesystem path for a file/dir within this plugin.
|
||||
*
|
||||
* @param string $file The path within this plugin, e.g. '/js/clever-fx.js'
|
||||
* @return string Filesystem path
|
||||
*/
|
||||
final public function plugin_path( $file = '' ) {
|
||||
return $this->_plugin( 'path', $file );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a version number for the given plugin file.
|
||||
*
|
||||
* @param string $file The path within this plugin, e.g. '/js/clever-fx.js'
|
||||
* @return string Version
|
||||
*/
|
||||
final public function plugin_ver( $file ) {
|
||||
return QM_VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current plugin's basename, eg. 'my_plugin/my_plugin.php'.
|
||||
*
|
||||
* @return string Basename
|
||||
*/
|
||||
final public function plugin_base() {
|
||||
return $this->_plugin( 'base' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates and returns the current plugin info.
|
||||
*
|
||||
* @param string $item
|
||||
* @param string $file
|
||||
* @return string
|
||||
*/
|
||||
private function _plugin( $item, $file = '' ) {
|
||||
if ( ! array_key_exists( $item, $this->plugin ) ) {
|
||||
switch ( $item ) {
|
||||
case 'url':
|
||||
$this->plugin[ $item ] = plugin_dir_url( $this->file );
|
||||
break;
|
||||
case 'path':
|
||||
$this->plugin[ $item ] = plugin_dir_path( $this->file );
|
||||
break;
|
||||
case 'base':
|
||||
$this->plugin[ $item ] = plugin_basename( $this->file );
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $this->plugin[ $item ] . ltrim( $file, '/' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name Icon name.
|
||||
* @return string Icon HTML.
|
||||
*/
|
||||
public static function icon( $name ) {
|
||||
if ( 'blank' === $name ) {
|
||||
return '<span class="qm-icon qm-icon-blank"></span>';
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
'<svg class="qm-icon qm-icon-%1$s" aria-hidden="true" width="20" height="20" viewBox="0 0 20 20"><use href="#qm-icon-%1$s" /></svg>',
|
||||
esc_attr( $name )
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
158
wp-content/plugins/query-monitor/classes/QM.php
Normal file
158
wp-content/plugins/query-monitor/classes/QM.php
Normal file
@ -0,0 +1,158 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* A convenience class for wrapping certain user-facing functionality.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM {
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param array<string, mixed> $context
|
||||
* @return void
|
||||
*/
|
||||
public static function emergency( $message, array $context = array() ) {
|
||||
/**
|
||||
* Fires when an `emergency` level message is logged.
|
||||
*
|
||||
* @since 3.1.0
|
||||
*
|
||||
* @param mixed $message The message or data to log.
|
||||
* @param array $context The context passed.
|
||||
*/
|
||||
do_action( 'qm/emergency', $message, $context );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param array<string, mixed> $context
|
||||
* @return void
|
||||
*/
|
||||
public static function alert( $message, array $context = array() ) {
|
||||
/**
|
||||
* Fires when an `alert` level message is logged.
|
||||
*
|
||||
* @since 3.1.0
|
||||
*
|
||||
* @param mixed $message The message or data to log.
|
||||
* @param array $context The context passed.
|
||||
*/
|
||||
do_action( 'qm/alert', $message, $context );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param array<string, mixed> $context
|
||||
* @return void
|
||||
*/
|
||||
public static function critical( $message, array $context = array() ) {
|
||||
/**
|
||||
* Fires when a `critical` level message is logged.
|
||||
*
|
||||
* @since 3.1.0
|
||||
*
|
||||
* @param mixed $message The message or data to log.
|
||||
* @param array $context The context passed.
|
||||
*/
|
||||
do_action( 'qm/critical', $message, $context );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param array<string, mixed> $context
|
||||
* @return void
|
||||
*/
|
||||
public static function error( $message, array $context = array() ) {
|
||||
/**
|
||||
* Fires when an `error` level message is logged.
|
||||
*
|
||||
* @since 3.1.0
|
||||
*
|
||||
* @param mixed $message The message or data to log.
|
||||
* @param array $context The context passed.
|
||||
*/
|
||||
do_action( 'qm/error', $message, $context );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param array<string, mixed> $context
|
||||
* @return void
|
||||
*/
|
||||
public static function warning( $message, array $context = array() ) {
|
||||
/**
|
||||
* Fires when a `warning` level message is logged.
|
||||
*
|
||||
* @since 3.1.0
|
||||
*
|
||||
* @param mixed $message The message or data to log.
|
||||
* @param array $context The context passed.
|
||||
*/
|
||||
do_action( 'qm/warning', $message, $context );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param array<string, mixed> $context
|
||||
* @return void
|
||||
*/
|
||||
public static function notice( $message, array $context = array() ) {
|
||||
/**
|
||||
* Fires when a `notice` level message is logged.
|
||||
*
|
||||
* @since 3.1.0
|
||||
*
|
||||
* @param mixed $message The message or data to log.
|
||||
* @param array $context The context passed.
|
||||
*/
|
||||
do_action( 'qm/notice', $message, $context );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param array<string, mixed> $context
|
||||
* @return void
|
||||
*/
|
||||
public static function info( $message, array $context = array() ) {
|
||||
/**
|
||||
* Fires when an `info` level message is logged.
|
||||
*
|
||||
* @since 3.1.0
|
||||
*
|
||||
* @param mixed $message The message or data to log.
|
||||
* @param array $context The context passed.
|
||||
*/
|
||||
do_action( 'qm/info', $message, $context );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param array<string, mixed> $context
|
||||
* @return void
|
||||
*/
|
||||
public static function debug( $message, array $context = array() ) {
|
||||
/**
|
||||
* Fires when a `debug` level message is logged.
|
||||
*
|
||||
* @since 3.1.0
|
||||
*
|
||||
* @param mixed $message The message or data to log.
|
||||
* @param array $context The context passed.
|
||||
*/
|
||||
do_action( 'qm/debug', $message, $context );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $level
|
||||
* @param string $message
|
||||
* @param array<string, mixed> $context
|
||||
* @phpstan-param QM_Collector_Logger::* $level
|
||||
* @return void
|
||||
*/
|
||||
public static function log( $level, $message, array $context = array() ) {
|
||||
/** @var QM_Collector_Logger */
|
||||
$logger = QM_Collectors::get( 'logger' );
|
||||
$logger->log( $level, $message, $context );
|
||||
}
|
||||
}
|
281
wp-content/plugins/query-monitor/classes/QueryMonitor.php
Normal file
281
wp-content/plugins/query-monitor/classes/QueryMonitor.php
Normal file
@ -0,0 +1,281 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* The main Query Monitor plugin class.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QueryMonitor extends QM_Plugin {
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function set_up() {
|
||||
|
||||
# Actions
|
||||
add_action( 'plugins_loaded', array( $this, 'action_plugins_loaded' ) );
|
||||
add_action( 'init', array( $this, 'action_init' ) );
|
||||
add_action( 'members_register_caps', array( $this, 'action_register_members_caps' ) );
|
||||
add_action( 'members_register_cap_groups', array( $this, 'action_register_members_groups' ) );
|
||||
add_action( 'qm/cease', array( $this, 'action_cease' ) );
|
||||
|
||||
# Filters
|
||||
add_filter( 'user_has_cap', array( $this, 'filter_user_has_cap' ), 10, 4 );
|
||||
add_filter( 'ure_built_in_wp_caps', array( $this, 'filter_ure_caps' ) );
|
||||
add_filter( 'ure_capabilities_groups_tree', array( $this, 'filter_ure_groups' ) );
|
||||
add_filter( 'network_admin_plugin_action_links_query-monitor/query-monitor.php', array( $this, 'filter_plugin_action_links' ) );
|
||||
add_filter( 'plugin_action_links_query-monitor/query-monitor.php', array( $this, 'filter_plugin_action_links' ) );
|
||||
add_filter( 'plugin_row_meta', array( $this, 'filter_plugin_row_meta' ), 10, 2 );
|
||||
|
||||
# Load and register built-in collectors:
|
||||
$collectors = array();
|
||||
$files = glob( $this->plugin_path( 'collectors/*.php' ) );
|
||||
if ( $files ) {
|
||||
foreach ( $files as $file ) {
|
||||
$key = basename( $file, '.php' );
|
||||
$collectors[ $key ] = $file;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow filtering of built-in collector files.
|
||||
*
|
||||
* @since 2.14.0
|
||||
*
|
||||
* @param array<string, string> $collectors Array of file paths to be loaded, keyed by the base
|
||||
* name of the file.
|
||||
*/
|
||||
foreach ( apply_filters( 'qm/built-in-collectors', $collectors ) as $file ) {
|
||||
include_once $file;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string> $actions
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function filter_plugin_action_links( array $actions ) {
|
||||
return array_merge( array(
|
||||
'settings' => '<a href="#qm-settings">' . esc_html__( 'Settings', 'query-monitor' ) . '</a>',
|
||||
'add-ons' => '<a href="https://github.com/johnbillion/query-monitor/wiki/Query-Monitor-Add-on-Plugins">' . esc_html__( 'Add-ons', 'query-monitor' ) . '</a>',
|
||||
), $actions );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the array of row meta for each plugin in the Plugins list table.
|
||||
*
|
||||
* @param array<int, string> $plugin_meta An array of the plugin's metadata.
|
||||
* @param string $plugin_file Path to the plugin file relative to the plugins directory.
|
||||
* @return array<int, string> Updated array of the plugin's metadata.
|
||||
*/
|
||||
public function filter_plugin_row_meta( array $plugin_meta, $plugin_file ) {
|
||||
if ( 'query-monitor/query-monitor.php' !== $plugin_file ) {
|
||||
return $plugin_meta;
|
||||
}
|
||||
|
||||
$plugin_meta[] = sprintf(
|
||||
'<a href="%1$s"><span class="dashicons dashicons-star-filled" aria-hidden="true" style="font-size:14px;line-height:1.3"></span>%2$s</a>',
|
||||
'https://github.com/sponsors/johnbillion',
|
||||
esc_html_x( 'Sponsor', 'verb', 'query-monitor' )
|
||||
);
|
||||
|
||||
return $plugin_meta;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter a user's capabilities so they can be altered at runtime.
|
||||
*
|
||||
* This is used to:
|
||||
* - Grant the 'view_query_monitor' capability to the user if they have the ability to manage options.
|
||||
*
|
||||
* This does not get called for Super Admins.
|
||||
*
|
||||
* @param array<string, bool> $user_caps Array of key/value pairs where keys represent a capability name and boolean values
|
||||
* represent whether the user has that capability.
|
||||
* @param array<int, string> $required_caps Required primitive capabilities for the requested capability.
|
||||
* @param mixed[] $args {
|
||||
* Arguments that accompany the requested capability check.
|
||||
*
|
||||
* @type string $0 Requested capability.
|
||||
* @type int $1 Concerned user ID.
|
||||
* @type mixed ...$2 Optional second and further parameters.
|
||||
* }
|
||||
* @phpstan-param array{
|
||||
* 0: string,
|
||||
* 1: int,
|
||||
* } $args
|
||||
* @param WP_User $user Concerned user object.
|
||||
* @return array<string, bool> Concerned user's capabilities.
|
||||
*/
|
||||
public function filter_user_has_cap( array $user_caps, array $required_caps, array $args, WP_User $user ) {
|
||||
if ( 'view_query_monitor' !== $args[0] ) {
|
||||
return $user_caps;
|
||||
}
|
||||
|
||||
if ( array_key_exists( 'view_query_monitor', $user_caps ) ) {
|
||||
return $user_caps;
|
||||
}
|
||||
|
||||
if ( ! is_multisite() && user_can( $args[1], 'manage_options' ) ) {
|
||||
$user_caps['view_query_monitor'] = true;
|
||||
}
|
||||
|
||||
return $user_caps;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function action_plugins_loaded() {
|
||||
// Hide QM itself from output by default:
|
||||
if ( ! defined( 'QM_HIDE_SELF' ) ) {
|
||||
define( 'QM_HIDE_SELF', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the collectors that are being added.
|
||||
*
|
||||
* @since 2.11.2
|
||||
*
|
||||
* @param array<int, QM_Collector> $collectors Array of collector instances.
|
||||
* @param QueryMonitor $instance QueryMonitor instance.
|
||||
*/
|
||||
foreach ( apply_filters( 'qm/collectors', array(), $this ) as $collector ) {
|
||||
QM_Collectors::add( $collector );
|
||||
}
|
||||
|
||||
# Load dispatchers:
|
||||
foreach ( (array) glob( $this->plugin_path( 'dispatchers/*.php' ) ) as $file ) {
|
||||
include_once $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the dispatchers that are being added.
|
||||
*
|
||||
* @since 2.11.2
|
||||
*
|
||||
* @param array<int, QM_Dispatcher> $dispatchers Array of dispatcher instances.
|
||||
* @param QueryMonitor $instance QueryMonitor instance.
|
||||
*/
|
||||
foreach ( apply_filters( 'qm/dispatchers', array(), $this ) as $dispatcher ) {
|
||||
QM_Dispatchers::add( $dispatcher );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function action_init() {
|
||||
load_plugin_textdomain( 'query-monitor', false, dirname( $this->plugin_base() ) . '/languages' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function symlink_warning() {
|
||||
$db = WP_CONTENT_DIR . '/db.php';
|
||||
trigger_error( sprintf(
|
||||
/* translators: %s: Symlink file location */
|
||||
esc_html__( 'The symlink at %s is no longer pointing to the correct location. Please remove the symlink, then deactivate and reactivate Query Monitor.', 'query-monitor' ),
|
||||
'<code>' . esc_html( $db ) . '</code>'
|
||||
), E_USER_WARNING );
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the Query Monitor user capability group for the Members plugin.
|
||||
*
|
||||
* @link https://wordpress.org/plugins/members/
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function action_register_members_groups() {
|
||||
members_register_cap_group( 'query_monitor', array(
|
||||
'label' => __( 'Query Monitor', 'query-monitor' ),
|
||||
'caps' => array(
|
||||
'view_query_monitor',
|
||||
),
|
||||
'icon' => 'dashicons-admin-tools',
|
||||
'priority' => 30,
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the View Query Monitor user capability for the Members plugin.
|
||||
*
|
||||
* @link https://wordpress.org/plugins/members/
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function action_register_members_caps() {
|
||||
members_register_cap( 'view_query_monitor', array(
|
||||
'label' => _x( 'View Query Monitor', 'Human readable label for the user capability required to view Query Monitor.', 'query-monitor' ),
|
||||
'group' => 'query_monitor',
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the Query Monitor user capability group for the User Role Editor plugin.
|
||||
*
|
||||
* @link https://wordpress.org/plugins/user-role-editor/
|
||||
*
|
||||
* @param array<string, array<string, mixed>> $groups Array of existing groups.
|
||||
* @return array<string, array<string, mixed>> Updated array of groups.
|
||||
*/
|
||||
public function filter_ure_groups( array $groups ) {
|
||||
$groups['query_monitor'] = array(
|
||||
'caption' => esc_html__( 'Query Monitor', 'query-monitor' ),
|
||||
'parent' => 'custom',
|
||||
'level' => 2,
|
||||
);
|
||||
|
||||
return $groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the View Query Monitor user capability for the User Role Editor plugin.
|
||||
*
|
||||
* @link https://wordpress.org/plugins/user-role-editor/
|
||||
*
|
||||
* @param array<string, array<int, string>> $caps Array of existing capabilities.
|
||||
* @return array<string, array<int, string>> Updated array of capabilities.
|
||||
*/
|
||||
public function filter_ure_caps( array $caps ) {
|
||||
$caps['view_query_monitor'] = array(
|
||||
'custom',
|
||||
'query_monitor',
|
||||
);
|
||||
|
||||
return $caps;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function action_cease() {
|
||||
// iterate collectors, call tear_down
|
||||
// discard all collected data
|
||||
QM_Collectors::cease();
|
||||
|
||||
// remove dispatchers or prevent them from doing anything
|
||||
QM_Dispatchers::cease();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
* @return self
|
||||
*/
|
||||
public static function init( $file = null ) {
|
||||
|
||||
static $instance = null;
|
||||
|
||||
if ( ! $instance ) {
|
||||
$instance = new QueryMonitor( $file );
|
||||
}
|
||||
|
||||
return $instance;
|
||||
|
||||
}
|
||||
|
||||
}
|
183
wp-content/plugins/query-monitor/classes/Timer.php
Normal file
183
wp-content/plugins/query-monitor/classes/Timer.php
Normal file
@ -0,0 +1,183 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Timer that collects timing and memory usage.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_Timer {
|
||||
|
||||
/**
|
||||
* @var array<string, mixed>
|
||||
* @phpstan-var array{
|
||||
* time: float,
|
||||
* memory: int,
|
||||
* data: mixed[]|null,
|
||||
* }
|
||||
*/
|
||||
protected $start;
|
||||
|
||||
/**
|
||||
* @var array<string, mixed>|null
|
||||
* @phpstan-var array{
|
||||
* time: float,
|
||||
* memory: int,
|
||||
* data: mixed[]|null,
|
||||
* }|null
|
||||
*/
|
||||
protected $end = null;
|
||||
|
||||
/**
|
||||
* @var QM_Backtrace
|
||||
*/
|
||||
protected $trace;
|
||||
|
||||
/**
|
||||
* @var array<string, array<string, mixed>>
|
||||
* @phpstan-var array<string, array{
|
||||
* time: float,
|
||||
* memory: int,
|
||||
* data: mixed[]|null,
|
||||
* }>
|
||||
*/
|
||||
protected $laps = array();
|
||||
|
||||
/**
|
||||
* @param mixed[] $data
|
||||
* @return self
|
||||
*/
|
||||
public function start( array $data = null ) {
|
||||
$this->trace = new QM_Backtrace();
|
||||
$this->start = array(
|
||||
'time' => microtime( true ),
|
||||
'memory' => memory_get_usage(),
|
||||
'data' => $data,
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $data
|
||||
* @return self
|
||||
*/
|
||||
public function stop( array $data = null ) {
|
||||
|
||||
$this->end = array(
|
||||
'time' => microtime( true ),
|
||||
'memory' => memory_get_usage(),
|
||||
'data' => $data,
|
||||
);
|
||||
|
||||
return $this;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $data
|
||||
* @param string $name
|
||||
* @return self
|
||||
*/
|
||||
public function lap( array $data = null, $name = null ) {
|
||||
|
||||
$lap = array(
|
||||
'time' => microtime( true ),
|
||||
'memory' => memory_get_usage(),
|
||||
'data' => $data,
|
||||
);
|
||||
|
||||
if ( ! isset( $name ) ) {
|
||||
$i = sprintf(
|
||||
/* translators: %s: Timing lap number */
|
||||
__( 'Lap %s', 'query-monitor' ),
|
||||
number_format_i18n( count( $this->laps ) + 1 )
|
||||
);
|
||||
} else {
|
||||
$i = $name;
|
||||
}
|
||||
|
||||
$this->laps[ $i ] = $lap;
|
||||
|
||||
return $this;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function get_laps() {
|
||||
|
||||
$laps = array();
|
||||
$prev = $this->start;
|
||||
|
||||
foreach ( $this->laps as $lap_id => $lap ) {
|
||||
|
||||
$lap['time_used'] = $lap['time'] - $prev['time'];
|
||||
$lap['memory_used'] = $lap['memory'] - $prev['memory'];
|
||||
|
||||
$laps[ $lap_id ] = $lap;
|
||||
$prev = $lap;
|
||||
|
||||
}
|
||||
|
||||
return $laps;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float
|
||||
*/
|
||||
public function get_time() {
|
||||
return $this->end['time'] - $this->start['time'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function get_memory() {
|
||||
return $this->end['memory'] - $this->start['memory'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float
|
||||
*/
|
||||
public function get_start_time() {
|
||||
return $this->start['time'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function get_start_memory() {
|
||||
return $this->start['memory'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float
|
||||
*/
|
||||
public function get_end_time() {
|
||||
return $this->end['time'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function get_end_memory() {
|
||||
return $this->end['memory'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return QM_Backtrace
|
||||
*/
|
||||
public function get_trace() {
|
||||
return $this->trace;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $data
|
||||
* @return self
|
||||
*/
|
||||
public function end( array $data = null ) {
|
||||
return $this->stop( $data );
|
||||
}
|
||||
|
||||
}
|
672
wp-content/plugins/query-monitor/classes/Util.php
Normal file
672
wp-content/plugins/query-monitor/classes/Util.php
Normal file
@ -0,0 +1,672 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* General utilities class.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! class_exists( 'QM_Util' ) ) {
|
||||
class QM_Util {
|
||||
|
||||
/**
|
||||
* @var array<string, QM_Component>
|
||||
*/
|
||||
protected static $file_components = array();
|
||||
|
||||
/**
|
||||
* @var array<string, string|null>
|
||||
*/
|
||||
protected static $file_dirs = array();
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected static $abspath = null;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected static $contentpath = null;
|
||||
|
||||
private function __construct() {}
|
||||
|
||||
/**
|
||||
* @param string $size
|
||||
* @return float
|
||||
*/
|
||||
public static function convert_hr_to_bytes( $size ) {
|
||||
|
||||
# Annoyingly, wp_convert_hr_to_bytes() is defined in a file that's only
|
||||
# loaded in the admin area, so we'll use our own version.
|
||||
# See also http://core.trac.wordpress.org/ticket/17725
|
||||
|
||||
$bytes = (float) $size;
|
||||
|
||||
if ( $bytes ) {
|
||||
$last = strtolower( substr( $size, -1 ) );
|
||||
$pos = strpos( ' kmg', $last, 1 );
|
||||
if ( $pos ) {
|
||||
$bytes *= pow( 1024, $pos );
|
||||
}
|
||||
$bytes = round( $bytes );
|
||||
}
|
||||
|
||||
return $bytes;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $dir
|
||||
* @param string $path_replace
|
||||
* @return string
|
||||
*/
|
||||
public static function standard_dir( $dir, $path_replace = null ) {
|
||||
|
||||
$dir = self::normalize_path( $dir );
|
||||
|
||||
if ( is_string( $path_replace ) ) {
|
||||
if ( ! self::$abspath ) {
|
||||
self::$abspath = self::normalize_path( ABSPATH );
|
||||
self::$contentpath = self::normalize_path( dirname( WP_CONTENT_DIR ) . '/' );
|
||||
}
|
||||
$dir = str_replace( array(
|
||||
self::$abspath,
|
||||
self::$contentpath,
|
||||
), $path_replace, $dir );
|
||||
}
|
||||
|
||||
return $dir;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
public static function normalize_path( $path ) {
|
||||
if ( function_exists( 'wp_normalize_path' ) ) {
|
||||
$path = wp_normalize_path( $path );
|
||||
} else {
|
||||
$path = str_replace( '\\', '/', $path );
|
||||
$path = str_replace( '//', '/', $path );
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string|null>
|
||||
*/
|
||||
public static function get_file_dirs() {
|
||||
if ( empty( self::$file_dirs ) ) {
|
||||
|
||||
/**
|
||||
* Filters the absolute directory paths that correlate to components.
|
||||
*
|
||||
* Note that this filter is applied before QM adds its built-in list of components. This is
|
||||
* so custom registered components take precedence during component detection.
|
||||
*
|
||||
* See also the corresponding filters:
|
||||
*
|
||||
* - `qm/component_context/{$type}`
|
||||
* - `qm/component_name/{$type}`
|
||||
*
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @param array<string, string|null> $dirs Array of absolute directory paths keyed by component identifier.
|
||||
*/
|
||||
self::$file_dirs = apply_filters( 'qm/component_dirs', self::$file_dirs );
|
||||
|
||||
self::$file_dirs['plugin'] = WP_PLUGIN_DIR;
|
||||
self::$file_dirs['mu-vendor'] = WPMU_PLUGIN_DIR . '/vendor';
|
||||
self::$file_dirs['go-plugin'] = WPMU_PLUGIN_DIR . '/shared-plugins';
|
||||
self::$file_dirs['mu-plugin'] = WPMU_PLUGIN_DIR;
|
||||
self::$file_dirs['vip-plugin'] = get_theme_root() . '/vip/plugins';
|
||||
|
||||
if ( defined( 'WPCOM_VIP_CLIENT_MU_PLUGIN_DIR' ) ) {
|
||||
self::$file_dirs['vip-client-mu-plugin'] = WPCOM_VIP_CLIENT_MU_PLUGIN_DIR;
|
||||
}
|
||||
|
||||
if ( defined( '\Altis\ROOT_DIR' ) ) {
|
||||
self::$file_dirs['altis-vendor'] = \Altis\ROOT_DIR . '/vendor';
|
||||
}
|
||||
|
||||
self::$file_dirs['theme'] = null;
|
||||
self::$file_dirs['stylesheet'] = get_stylesheet_directory();
|
||||
self::$file_dirs['template'] = get_template_directory();
|
||||
self::$file_dirs['other'] = WP_CONTENT_DIR;
|
||||
self::$file_dirs['core'] = ABSPATH;
|
||||
self::$file_dirs['unknown'] = null;
|
||||
|
||||
foreach ( self::$file_dirs as $type => $dir ) {
|
||||
if ( null === $dir ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
self::$file_dirs[ $type ] = self::standard_dir( $dir );
|
||||
}
|
||||
}
|
||||
|
||||
return self::$file_dirs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to determine the component responsible for a given file name.
|
||||
*
|
||||
* @param string $file An absolute file path.
|
||||
* @return QM_Component An object representing the component.
|
||||
*/
|
||||
public static function get_file_component( $file ) {
|
||||
$file = self::standard_dir( $file );
|
||||
$type = '';
|
||||
|
||||
if ( isset( self::$file_components[ $file ] ) ) {
|
||||
return self::$file_components[ $file ];
|
||||
}
|
||||
|
||||
foreach ( self::get_file_dirs() as $type => $dir ) {
|
||||
// this slash makes paths such as plugins-mu match mu-plugin not plugin
|
||||
if ( $dir && ( 0 === strpos( $file, trailingslashit( $dir ) ) ) ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$context = $type;
|
||||
|
||||
switch ( $type ) {
|
||||
case 'altis-vendor':
|
||||
$plug = str_replace( \Altis\ROOT_DIR . '/vendor/', '', $file );
|
||||
$plug = explode( '/', $plug, 3 );
|
||||
$plug = $plug[0] . '/' . $plug[1];
|
||||
/* translators: %s: Dependency name */
|
||||
$name = sprintf( __( 'Dependency: %s', 'query-monitor' ), $plug );
|
||||
break;
|
||||
case 'plugin':
|
||||
case 'mu-plugin':
|
||||
case 'mu-vendor':
|
||||
$plug = str_replace( '/vendor/', '/', $file );
|
||||
$plug = plugin_basename( $plug );
|
||||
if ( strpos( $plug, '/' ) ) {
|
||||
$plug = explode( '/', $plug );
|
||||
$plug = reset( $plug );
|
||||
} else {
|
||||
$plug = basename( $plug );
|
||||
}
|
||||
if ( 'plugin' !== $type ) {
|
||||
/* translators: %s: Plugin name */
|
||||
$name = sprintf( __( 'MU Plugin: %s', 'query-monitor' ), $plug );
|
||||
} else {
|
||||
/* translators: %s: Plugin name */
|
||||
$name = sprintf( __( 'Plugin: %s', 'query-monitor' ), $plug );
|
||||
}
|
||||
$context = $plug;
|
||||
break;
|
||||
case 'go-plugin':
|
||||
case 'vip-plugin':
|
||||
case 'vip-client-mu-plugin':
|
||||
$plug = str_replace( self::$file_dirs[ $type ], '', $file );
|
||||
$plug = trim( $plug, '/' );
|
||||
if ( strpos( $plug, '/' ) ) {
|
||||
$plug = explode( '/', $plug );
|
||||
$plug = reset( $plug );
|
||||
} else {
|
||||
$plug = basename( $plug );
|
||||
}
|
||||
if ( 'vip-client-mu-plugin' === $type ) {
|
||||
/* translators: %s: Plugin name */
|
||||
$name = sprintf( __( 'VIP Client MU Plugin: %s', 'query-monitor' ), $plug );
|
||||
} else {
|
||||
/* translators: %s: Plugin name */
|
||||
$name = sprintf( __( 'VIP Plugin: %s', 'query-monitor' ), $plug );
|
||||
}
|
||||
$context = $plug;
|
||||
break;
|
||||
case 'stylesheet':
|
||||
if ( is_child_theme() ) {
|
||||
$name = __( 'Child Theme', 'query-monitor' );
|
||||
} else {
|
||||
$name = __( 'Theme', 'query-monitor' );
|
||||
}
|
||||
$type = 'theme';
|
||||
break;
|
||||
case 'template':
|
||||
$name = __( 'Parent Theme', 'query-monitor' );
|
||||
$type = 'theme';
|
||||
break;
|
||||
case 'other':
|
||||
// Anything else that's within the content directory should appear as
|
||||
// `wp-content/{dir}` or `wp-content/{file}`
|
||||
$name = self::standard_dir( $file );
|
||||
$name = str_replace( dirname( self::$file_dirs['other'] ), '', $name );
|
||||
$parts = explode( '/', trim( $name, '/' ) );
|
||||
$name = $parts[0] . '/' . $parts[1];
|
||||
$context = $file;
|
||||
break;
|
||||
case 'core':
|
||||
$name = __( 'WordPress Core', 'query-monitor' );
|
||||
break;
|
||||
case 'unknown':
|
||||
default:
|
||||
$name = __( 'Unknown', 'query-monitor' );
|
||||
|
||||
/**
|
||||
* Filters the type of a custom or unknown component.
|
||||
*
|
||||
* The dynamic portion of the hook name, `$type`, refers to the component identifier.
|
||||
*
|
||||
* See also the corresponding filters:
|
||||
*
|
||||
* - `qm/component_dirs`
|
||||
* - `qm/component_name/{$type}`
|
||||
* - `qm/component_context/{$type}`
|
||||
*
|
||||
* @since 3.8.1
|
||||
*
|
||||
* @param string $type The component type.
|
||||
* @param string $file The full file path for the file within the component.
|
||||
* @param string $name The component name.
|
||||
* @param string $context The context for the component.
|
||||
*/
|
||||
$type = apply_filters( "qm/component_type/{$type}", $type, $file, $name, $context );
|
||||
|
||||
/**
|
||||
* Filters the name of a custom or unknown component.
|
||||
*
|
||||
* The dynamic portion of the hook name, `$type`, refers to the component identifier.
|
||||
*
|
||||
* See also the corresponding filters:
|
||||
*
|
||||
* - `qm/component_dirs`
|
||||
* - `qm/component_type/{$type}`
|
||||
* - `qm/component_context/{$type}`
|
||||
*
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @param string $name The component name.
|
||||
* @param string $file The full file path for the file within the component.
|
||||
*/
|
||||
$name = apply_filters( "qm/component_name/{$type}", $name, $file );
|
||||
|
||||
/**
|
||||
* Filters the context for a custom or unknown component. The context is usually a
|
||||
* representation of its type more specific to the individual component.
|
||||
*
|
||||
* The dynamic portion of the hook name, `$type`, refers to the component identifier.
|
||||
*
|
||||
* See also the corresponding filters:
|
||||
*
|
||||
* - `qm/component_dirs`
|
||||
* - `qm/component_type/{$type}`
|
||||
* - `qm/component_name/{$type}`
|
||||
*
|
||||
* @since 3.8.0
|
||||
*
|
||||
* @param string $context The context for the component.
|
||||
* @param string $file The full file path for the file within the component.
|
||||
* @param string $name The component name.
|
||||
*/
|
||||
$context = apply_filters( "qm/component_context/{$type}", $context, $file, $name );
|
||||
break;
|
||||
}
|
||||
|
||||
$component = new QM_Component();
|
||||
$component->type = $type;
|
||||
$component->name = $name;
|
||||
$component->context = $context;
|
||||
|
||||
self::$file_components[ $file ] = $component;
|
||||
|
||||
return self::$file_components[ $file ];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $callback
|
||||
* @return array<string, mixed>
|
||||
* @phpstan-return array{
|
||||
* name?: string,
|
||||
* file?: string|false,
|
||||
* line?: string|false,
|
||||
* error?: WP_Error,
|
||||
* component?: QM_Component,
|
||||
* }
|
||||
*/
|
||||
public static function populate_callback( array $callback ) {
|
||||
|
||||
if ( is_string( $callback['function'] ) && ( false !== strpos( $callback['function'], '::' ) ) ) {
|
||||
$callback['function'] = explode( '::', $callback['function'] );
|
||||
}
|
||||
|
||||
if ( isset( $callback['class'] ) ) {
|
||||
$callback['function'] = array(
|
||||
$callback['class'],
|
||||
$callback['function'],
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
if ( is_array( $callback['function'] ) ) {
|
||||
if ( is_object( $callback['function'][0] ) ) {
|
||||
$class = get_class( $callback['function'][0] );
|
||||
$access = '->';
|
||||
} else {
|
||||
$class = $callback['function'][0];
|
||||
$access = '::';
|
||||
}
|
||||
|
||||
$callback['name'] = self::shorten_fqn( $class . $access . $callback['function'][1] ) . '()';
|
||||
$ref = new ReflectionMethod( $class, $callback['function'][1] );
|
||||
} elseif ( is_object( $callback['function'] ) ) {
|
||||
if ( $callback['function'] instanceof Closure ) {
|
||||
$ref = new ReflectionFunction( $callback['function'] );
|
||||
$filename = $ref->getFileName();
|
||||
|
||||
if ( $filename ) {
|
||||
$file = self::standard_dir( $filename, '' );
|
||||
if ( 0 === strpos( $file, '/' ) ) {
|
||||
$file = basename( $filename );
|
||||
}
|
||||
$callback['name'] = sprintf(
|
||||
/* translators: A closure is an anonymous PHP function. 1: Line number, 2: File name */
|
||||
__( 'Closure on line %1$d of %2$s', 'query-monitor' ),
|
||||
$ref->getStartLine(),
|
||||
$file
|
||||
);
|
||||
} else {
|
||||
/* translators: A closure is an anonymous PHP function */
|
||||
$callback['name'] = __( 'Unknown closure', 'query-monitor' );
|
||||
}
|
||||
} else {
|
||||
// the object should have a __invoke() method
|
||||
$class = get_class( $callback['function'] );
|
||||
$callback['name'] = self::shorten_fqn( $class ) . '->__invoke()';
|
||||
$ref = new ReflectionMethod( $class, '__invoke' );
|
||||
}
|
||||
} else {
|
||||
$callback['name'] = self::shorten_fqn( $callback['function'] ) . '()';
|
||||
$ref = new ReflectionFunction( $callback['function'] );
|
||||
}
|
||||
|
||||
$callback['file'] = $ref->getFileName();
|
||||
$callback['line'] = $ref->getStartLine();
|
||||
|
||||
// https://github.com/facebook/hhvm/issues/5856
|
||||
$name = trim( $ref->getName() );
|
||||
|
||||
if ( '__lambda_func' === $name || 0 === strpos( $name, 'lambda_' ) ) {
|
||||
if ( $callback['file'] && preg_match( '|(?P<file>.*)\((?P<line>[0-9]+)\)|', $callback['file'], $matches ) ) {
|
||||
$callback['file'] = $matches['file'];
|
||||
$callback['line'] = $matches['line'];
|
||||
$file = trim( self::standard_dir( $callback['file'], '' ), '/' );
|
||||
/* translators: 1: Line number, 2: File name */
|
||||
$callback['name'] = sprintf( __( 'Anonymous function on line %1$d of %2$s', 'query-monitor' ), $callback['line'], $file );
|
||||
} else {
|
||||
// https://github.com/facebook/hhvm/issues/5807
|
||||
unset( $callback['line'], $callback['file'] );
|
||||
$callback['name'] = $name . '()';
|
||||
$callback['error'] = new WP_Error( 'unknown_lambda', __( 'Unable to determine source of lambda function', 'query-monitor' ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $callback['file'] ) ) {
|
||||
$callback['component'] = self::get_file_component( $callback['file'] );
|
||||
} else {
|
||||
$callback['component'] = new QM_Component();
|
||||
$callback['component']->type = 'php';
|
||||
$callback['component']->name = 'PHP';
|
||||
$callback['component']->context = '';
|
||||
}
|
||||
} catch ( ReflectionException $e ) {
|
||||
|
||||
$callback['error'] = new WP_Error( 'reflection_exception', $e->getMessage() );
|
||||
|
||||
}
|
||||
|
||||
unset( $callback['function'], $callback['class'] );
|
||||
|
||||
return $callback;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_ajax() {
|
||||
if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_async() {
|
||||
if ( self::is_ajax() ) {
|
||||
return true;
|
||||
}
|
||||
if ( isset( $_SERVER['HTTP_X_REQUESTED_WITH'] ) && 'xmlhttprequest' === strtolower( $_SERVER['HTTP_X_REQUESTED_WITH'] ) ) { // phpcs:ignore
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WP_Role|false
|
||||
*/
|
||||
public static function get_admins() {
|
||||
if ( is_multisite() ) {
|
||||
return false;
|
||||
} else {
|
||||
return get_role( 'administrator' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_multi_network() {
|
||||
return ( function_exists( 'is_multi_network' ) && is_multi_network() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|string $client
|
||||
* @return array<string, int>
|
||||
* @phpstan-return array{
|
||||
* major: int,
|
||||
* minor: int,
|
||||
* patch: int,
|
||||
* }
|
||||
*/
|
||||
public static function get_client_version( $client ) {
|
||||
|
||||
$client = intval( $client );
|
||||
|
||||
$hello = $client % 10000;
|
||||
|
||||
$major = intval( floor( $client / 10000 ) );
|
||||
$minor = intval( floor( $hello / 100 ) );
|
||||
$patch = intval( $hello % 100 );
|
||||
|
||||
return compact( 'major', 'minor', 'patch' );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sql
|
||||
* @return string
|
||||
*/
|
||||
public static function get_query_type( $sql ) {
|
||||
// Trim leading whitespace and brackets
|
||||
$sql = ltrim( $sql, ' \t\n\r\0\x0B(' );
|
||||
|
||||
if ( 0 === strpos( $sql, '/*' ) ) {
|
||||
// Strip out leading comments such as `/*NO_SELECT_FOUND_ROWS*/` before calculating the query type
|
||||
$sql = preg_replace( '|^/\*[^\*/]+\*/|', '', $sql );
|
||||
}
|
||||
|
||||
$words = preg_split( '/\b/', trim( $sql ), 2, PREG_SPLIT_NO_EMPTY );
|
||||
$type = 'Unknown';
|
||||
|
||||
if ( is_array( $words ) && isset( $words[0] ) ) {
|
||||
$type = strtoupper( $words[0] );
|
||||
}
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @return string|float|int
|
||||
*/
|
||||
public static function display_variable( $value ) {
|
||||
if ( is_string( $value ) ) {
|
||||
return $value;
|
||||
} elseif ( $value === null ) {
|
||||
return 'null';
|
||||
} elseif ( is_bool( $value ) ) {
|
||||
return ( $value ) ? 'true' : 'false';
|
||||
} elseif ( is_scalar( $value ) ) {
|
||||
return $value;
|
||||
} elseif ( is_object( $value ) ) {
|
||||
$class = get_class( $value );
|
||||
|
||||
switch ( true ) {
|
||||
|
||||
case ( $value instanceof WP_Post ):
|
||||
case ( $value instanceof WP_User ):
|
||||
$class = sprintf( '%s (ID: %s)', $class, $value->ID );
|
||||
break;
|
||||
|
||||
case ( $value instanceof WP_Term ):
|
||||
$class = sprintf( '%s (term_id: %s)', $class, $value->term_id );
|
||||
break;
|
||||
|
||||
case ( $value instanceof WP_Comment ):
|
||||
$class = sprintf( '%s (comment_ID: %s)', $class, $value->comment_ID );
|
||||
break;
|
||||
|
||||
case ( $value instanceof WP_Error ):
|
||||
$class = sprintf( '%s (%s)', $class, $value->get_error_code() );
|
||||
break;
|
||||
|
||||
case ( $value instanceof WP_Role ):
|
||||
case ( $value instanceof WP_Post_Type ):
|
||||
case ( $value instanceof WP_Taxonomy ):
|
||||
$class = sprintf( '%s (%s)', $class, $value->name );
|
||||
break;
|
||||
|
||||
case ( $value instanceof WP_Network ):
|
||||
$class = sprintf( '%s (id: %s)', $class, $value->id );
|
||||
break;
|
||||
|
||||
case ( $value instanceof WP_Site ):
|
||||
$class = sprintf( '%s (blog_id: %s)', $class, $value->blog_id );
|
||||
break;
|
||||
|
||||
case ( $value instanceof WP_Theme ):
|
||||
$class = sprintf( '%s (%s)', $class, $value->get_stylesheet() );
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return $class;
|
||||
} else {
|
||||
return gettype( $value );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortens a fully qualified name to reduce the length of the names of long namespaced symbols.
|
||||
*
|
||||
* This initialises portions that do not form the first or last portion of the name. For example:
|
||||
*
|
||||
* Inpsyde\Wonolog\HookListener\HookListenersRegistry->hook_callback()
|
||||
*
|
||||
* becomes:
|
||||
*
|
||||
* Inpsyde\W\H\HookListenersRegistry->hook_callback()
|
||||
*
|
||||
* @param string $fqn A fully qualified name.
|
||||
* @return string A shortened version of the name.
|
||||
*/
|
||||
public static function shorten_fqn( $fqn ) {
|
||||
if ( substr_count( $fqn, '\\' ) < 3 ) {
|
||||
return $fqn;
|
||||
}
|
||||
|
||||
return preg_replace_callback( '#\\\\[a-zA-Z0-9_\\\\]{4,}\\\\#', function( array $matches ) {
|
||||
preg_match_all( '#\\\\([a-zA-Z0-9_])#', $matches[0], $m );
|
||||
return '\\' . implode( '\\', $m[1] ) . '\\';
|
||||
}, $fqn );
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for JSON encoding data and formatting it in a consistent manner.
|
||||
*
|
||||
* @param mixed $data The data to be JSON encoded.
|
||||
* @return string The JSON encoded data.
|
||||
*/
|
||||
public static function json_format( $data ) {
|
||||
// phpcs:ignore PHPCompatibility.Constants.NewConstants.json_unescaped_slashesFound
|
||||
$json_options = JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES;
|
||||
|
||||
$json = json_encode( $data, $json_options );
|
||||
|
||||
if ( false === $json ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the site editor URL for a given template or template part name.
|
||||
*
|
||||
* @param string $template The site template name, for example `twentytwentytwo//header-small-dark`.
|
||||
* @param string $type The template type, either 'wp_template_part' or 'wp_template'.
|
||||
* @return string The admin URL for editing the site template.
|
||||
*/
|
||||
public static function get_site_editor_url( string $template, string $type = 'wp_template_part' ): string {
|
||||
return add_query_arg(
|
||||
array(
|
||||
'postType' => $type,
|
||||
'postId' => urlencode( $template ),
|
||||
'canvas' => 'edit',
|
||||
),
|
||||
admin_url( 'site-editor.php' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @param mixed $data
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_stringy( $data ) {
|
||||
return ( is_string( $data ) || ( is_object( $data ) && method_exists( $data, '__toString' ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $array
|
||||
* @param string $field
|
||||
* @return void
|
||||
*/
|
||||
public static function sort( array &$array, $field ) {
|
||||
usort( $array, function( array $a, array $b ) use ( $field ): int {
|
||||
return $a[ $field ] <=> $b[ $field ];
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $array
|
||||
* @param string $field
|
||||
* @return void
|
||||
*/
|
||||
public static function rsort( array &$array, $field ) {
|
||||
usort( $array, function( array $a, array $b ) use ( $field ): int {
|
||||
return $b[ $field ] <=> $a[ $field ];
|
||||
} );
|
||||
}
|
||||
}
|
||||
}
|
78
wp-content/plugins/query-monitor/classes/debug_bar.php
Normal file
78
wp-content/plugins/query-monitor/classes/debug_bar.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Mock 'Debug Bar' plugin class.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class Debug_Bar {
|
||||
/**
|
||||
* @var array<int, Debug_Bar_Panel>
|
||||
*/
|
||||
public $panels = array();
|
||||
|
||||
public function __construct() {
|
||||
add_action( 'wp_head', array( $this, 'ensure_ajaxurl' ), 1 );
|
||||
|
||||
$this->enqueue();
|
||||
$this->init_panels();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue() {
|
||||
// phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
|
||||
wp_register_style( 'debug-bar', false, array(
|
||||
'query-monitor',
|
||||
) );
|
||||
// phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
|
||||
wp_register_script( 'debug-bar', false, array(
|
||||
'query-monitor',
|
||||
) );
|
||||
|
||||
/**
|
||||
* Fires after scripts have been enqueued. This mimics the same action fired in the Debug Bar plugin.
|
||||
*
|
||||
* @since 2.7.0
|
||||
*/
|
||||
do_action( 'debug_bar_enqueue_scripts' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function init_panels() {
|
||||
/**
|
||||
* Filters the debug bar panel list. This mimics the same filter called in the Debug Bar plugin.
|
||||
*
|
||||
* @since 2.7.0
|
||||
*
|
||||
* @param array<int, Debug_Bar_Panel> $panels Array of Debug Bar panel instances.
|
||||
*/
|
||||
$this->panels = apply_filters( 'debug_bar_panels', array() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function ensure_ajaxurl() {
|
||||
$dispatcher = QM_Dispatchers::get( 'html' );
|
||||
|
||||
if ( $this->panels && $dispatcher && $dispatcher::user_can_view() ) {
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
var ajaxurl = '<?php echo esc_url( admin_url( 'admin-ajax.php' ) ); ?>';
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function Debug_Bar() {
|
||||
self::__construct();
|
||||
}
|
||||
|
||||
}
|
95
wp-content/plugins/query-monitor/classes/debug_bar_panel.php
Normal file
95
wp-content/plugins/query-monitor/classes/debug_bar_panel.php
Normal file
@ -0,0 +1,95 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Mock 'Debug Bar' panel class.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
abstract class Debug_Bar_Panel {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $_title = '';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $_visible = true;
|
||||
|
||||
/**
|
||||
* @param string $title
|
||||
*/
|
||||
public function __construct( $title = '' ) {
|
||||
$this->title( $title );
|
||||
|
||||
if ( $this->init() === false ) {
|
||||
$this->set_visible( false );
|
||||
return;
|
||||
}
|
||||
|
||||
add_filter( 'debug_bar_classes', array( $this, 'debug_bar_classes' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the panel.
|
||||
*
|
||||
* @return false|void
|
||||
*/
|
||||
public function init() {}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function prerender() {}
|
||||
|
||||
/**
|
||||
* Renders the panel.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function render() {}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function is_visible() {
|
||||
return $this->_visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $visible
|
||||
* @return void
|
||||
*/
|
||||
public function set_visible( $visible ) {
|
||||
$this->_visible = $visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $title
|
||||
* @return string|void
|
||||
*/
|
||||
public function title( $title = null ) {
|
||||
if ( ! isset( $title ) ) {
|
||||
return $this->_title;
|
||||
}
|
||||
$this->_title = $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, string> $classes
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function debug_bar_classes( $classes ) {
|
||||
return $classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $title
|
||||
* @return void
|
||||
*/
|
||||
public function Debug_Bar_Panel( $title = '' ) {
|
||||
self::__construct( $title );
|
||||
}
|
||||
|
||||
}
|
141
wp-content/plugins/query-monitor/collectors/admin.php
Normal file
141
wp-content/plugins/query-monitor/collectors/admin.php
Normal file
@ -0,0 +1,141 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Admin screen collector.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @extends QM_DataCollector<QM_Data_Admin>
|
||||
*/
|
||||
class QM_Collector_Admin extends QM_DataCollector {
|
||||
|
||||
public $id = 'response';
|
||||
|
||||
public function get_storage(): QM_Data {
|
||||
return new QM_Data_Admin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_concerned_actions() {
|
||||
$actions = array(
|
||||
'current_screen',
|
||||
'admin_notices',
|
||||
'all_admin_notices',
|
||||
'network_admin_notices',
|
||||
'user_admin_notices',
|
||||
);
|
||||
|
||||
if ( ! empty( $this->data->list_table ) ) {
|
||||
$actions[] = $this->data->list_table['column_action'];
|
||||
}
|
||||
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_concerned_filters() {
|
||||
$filters = array();
|
||||
|
||||
if ( ! empty( $this->data->list_table ) ) {
|
||||
$filters[] = $this->data->list_table['columns_filter'];
|
||||
$filters[] = $this->data->list_table['sortables_filter'];
|
||||
}
|
||||
|
||||
return $filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function process() {
|
||||
/**
|
||||
* @var string $pagenow
|
||||
* @var ?WP_List_Table $wp_list_table
|
||||
*/
|
||||
global $pagenow, $wp_list_table;
|
||||
|
||||
$this->data->pagenow = $pagenow;
|
||||
$this->data->typenow = $GLOBALS['typenow'] ?? '';
|
||||
$this->data->taxnow = $GLOBALS['taxnow'] ?? '';
|
||||
$this->data->hook_suffix = $GLOBALS['hook_suffix'] ?? '';
|
||||
$this->data->current_screen = get_current_screen();
|
||||
|
||||
$screens = array(
|
||||
'edit' => true,
|
||||
'edit-comments' => true,
|
||||
'edit-tags' => true,
|
||||
'link-manager' => true,
|
||||
'plugins' => true,
|
||||
'plugins-network' => true,
|
||||
'sites-network' => true,
|
||||
'themes-network' => true,
|
||||
'upload' => true,
|
||||
'users' => true,
|
||||
'users-network' => true,
|
||||
);
|
||||
|
||||
if ( empty( $this->data->current_screen ) || ! isset( $screens[ $this->data->current_screen->base ] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
# And now, WordPress' legendary inconsistency comes into play:
|
||||
|
||||
$columns = $this->data->current_screen->id;
|
||||
$sortables = $this->data->current_screen->id;
|
||||
$column = $this->data->current_screen->base;
|
||||
|
||||
if ( ! empty( $this->data->current_screen->taxonomy ) ) {
|
||||
$column = $this->data->current_screen->taxonomy;
|
||||
} elseif ( ! empty( $this->data->current_screen->post_type ) ) {
|
||||
$column = $this->data->current_screen->post_type . '_posts';
|
||||
}
|
||||
|
||||
if ( ! empty( $this->data->current_screen->post_type ) && empty( $this->data->current_screen->taxonomy ) ) {
|
||||
$columns = $this->data->current_screen->post_type . '_posts';
|
||||
}
|
||||
|
||||
if ( 'edit-comments' === $column ) {
|
||||
$column = 'comments';
|
||||
} elseif ( 'upload' === $column ) {
|
||||
$column = 'media';
|
||||
} elseif ( 'link-manager' === $column ) {
|
||||
$column = 'link';
|
||||
}
|
||||
|
||||
$list_table_data = array(
|
||||
'columns_filter' => "manage_{$columns}_columns",
|
||||
'sortables_filter' => "manage_{$sortables}_sortable_columns",
|
||||
'column_action' => "manage_{$column}_custom_column",
|
||||
);
|
||||
|
||||
if ( ! empty( $wp_list_table ) ) {
|
||||
$list_table_data['class_name'] = get_class( $wp_list_table );
|
||||
}
|
||||
|
||||
$this->data->list_table = $list_table_data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, QM_Collector> $collectors
|
||||
* @param QueryMonitor $qm
|
||||
* @return array<string, QM_Collector>
|
||||
*/
|
||||
function register_qm_collector_admin( array $collectors, QueryMonitor $qm ) {
|
||||
$collectors['response'] = new QM_Collector_Admin();
|
||||
return $collectors;
|
||||
}
|
||||
|
||||
if ( is_admin() ) {
|
||||
add_filter( 'qm/collectors', 'register_qm_collector_admin', 10, 2 );
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Enqueued scripts collector.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class QM_Collector_Assets_Scripts extends QM_Collector_Assets {
|
||||
|
||||
public $id = 'assets_scripts';
|
||||
|
||||
public function get_dependency_type() {
|
||||
return 'scripts';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_concerned_actions() {
|
||||
if ( is_admin() ) {
|
||||
return array(
|
||||
'admin_enqueue_scripts',
|
||||
'admin_print_footer_scripts',
|
||||
'admin_print_scripts',
|
||||
);
|
||||
} else {
|
||||
return array(
|
||||
'wp_enqueue_scripts',
|
||||
'wp_print_footer_scripts',
|
||||
'wp_print_scripts',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_concerned_filters() {
|
||||
return array(
|
||||
'print_scripts_array',
|
||||
'script_loader_src',
|
||||
'script_loader_tag',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, QM_Collector> $collectors
|
||||
* @param QueryMonitor $qm
|
||||
* @return array<string, QM_Collector>
|
||||
*/
|
||||
function register_qm_collector_assets_scripts( array $collectors, QueryMonitor $qm ) {
|
||||
$collectors['assets_scripts'] = new QM_Collector_Assets_Scripts();
|
||||
return $collectors;
|
||||
}
|
||||
|
||||
add_filter( 'qm/collectors', 'register_qm_collector_assets_scripts', 10, 2 );
|
@ -0,0 +1,42 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Enqueued styles collector.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class QM_Collector_Assets_Styles extends QM_Collector_Assets {
|
||||
|
||||
public $id = 'assets_styles';
|
||||
|
||||
public function get_dependency_type() {
|
||||
return 'styles';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_concerned_filters() {
|
||||
return array(
|
||||
'print_styles_array',
|
||||
'style_loader_src',
|
||||
'style_loader_tag',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, QM_Collector> $collectors
|
||||
* @param QueryMonitor $qm
|
||||
* @return array<string, QM_Collector>
|
||||
*/
|
||||
function register_qm_collector_assets_styles( array $collectors, QueryMonitor $qm ) {
|
||||
$collectors['assets_styles'] = new QM_Collector_Assets_Styles();
|
||||
return $collectors;
|
||||
}
|
||||
|
||||
add_filter( 'qm/collectors', 'register_qm_collector_assets_styles', 10, 2 );
|
266
wp-content/plugins/query-monitor/collectors/block_editor.php
Normal file
266
wp-content/plugins/query-monitor/collectors/block_editor.php
Normal file
@ -0,0 +1,266 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Block editor (née Gutenberg) collector.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @extends QM_DataCollector<QM_Data_Block_Editor>
|
||||
*/
|
||||
class QM_Collector_Block_Editor extends QM_DataCollector {
|
||||
|
||||
public $id = 'block_editor';
|
||||
|
||||
/**
|
||||
* @var array<int, mixed[]>
|
||||
*/
|
||||
protected $block_context = array();
|
||||
|
||||
/**
|
||||
* @var array<int, QM_Timer|false>
|
||||
*/
|
||||
protected $block_timing = array();
|
||||
|
||||
/**
|
||||
* @var QM_Timer|null
|
||||
*/
|
||||
protected $block_timer = null;
|
||||
|
||||
public function get_storage(): QM_Data {
|
||||
return new QM_Data_Block_Editor();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function set_up() {
|
||||
parent::set_up();
|
||||
|
||||
add_filter( 'pre_render_block', array( $this, 'filter_pre_render_block' ), 9999, 2 );
|
||||
add_filter( 'render_block_context', array( $this, 'filter_render_block_context' ), -9999, 2 );
|
||||
add_filter( 'render_block_data', array( $this, 'filter_render_block_data' ), -9999 );
|
||||
add_filter( 'render_block', array( $this, 'filter_render_block' ), 9999, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function tear_down() {
|
||||
remove_filter( 'pre_render_block', array( $this, 'filter_pre_render_block' ), 9999 );
|
||||
remove_filter( 'render_block_context', array( $this, 'filter_render_block_context' ), -9999 );
|
||||
remove_filter( 'render_block_data', array( $this, 'filter_render_block_data' ), -9999 );
|
||||
remove_filter( 'render_block', array( $this, 'filter_render_block' ), 9999 );
|
||||
|
||||
parent::tear_down();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_concerned_filters() {
|
||||
return array(
|
||||
'allowed_block_types',
|
||||
'allowed_block_types_all',
|
||||
'block_editor_settings_all',
|
||||
'block_type_metadata',
|
||||
'block_type_metadata_settings',
|
||||
'block_parser_class',
|
||||
'pre_render_block',
|
||||
'register_block_type_args',
|
||||
'render_block_context',
|
||||
'render_block_data',
|
||||
'render_block',
|
||||
'use_widgets_block_editor',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $pre_render
|
||||
* @param mixed[] $block
|
||||
* @return string|null
|
||||
*/
|
||||
public function filter_pre_render_block( $pre_render, array $block ) {
|
||||
if ( null !== $pre_render ) {
|
||||
$this->block_timing[] = false;
|
||||
}
|
||||
|
||||
return $pre_render;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $context
|
||||
* @param mixed[] $block
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function filter_render_block_context( array $context, array $block ) {
|
||||
$this->block_context[] = $context;
|
||||
|
||||
return $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $block
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function filter_render_block_data( array $block ) {
|
||||
$this->block_timer = new QM_Timer();
|
||||
$this->block_timer->start();
|
||||
|
||||
return $block;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $block_content
|
||||
* @param mixed[] $block
|
||||
* @return string
|
||||
*/
|
||||
public function filter_render_block( $block_content, array $block ) {
|
||||
if ( isset( $this->block_timer ) ) {
|
||||
$this->block_timing[] = $this->block_timer->stop();
|
||||
}
|
||||
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
public function process() {
|
||||
global $_wp_current_template_content;
|
||||
|
||||
$this->data->block_editor_enabled = self::wp_block_editor_enabled();
|
||||
|
||||
if ( ! empty( $_wp_current_template_content ) ) {
|
||||
// Full site editor:
|
||||
$content = $_wp_current_template_content;
|
||||
} elseif ( is_singular() ) {
|
||||
// Post editor:
|
||||
$content = get_post( get_queried_object_id() )->post_content;
|
||||
} else {
|
||||
// Nada:
|
||||
return;
|
||||
}
|
||||
|
||||
$this->data->post_has_blocks = self::wp_has_blocks( $content );
|
||||
$this->data->post_blocks = self::wp_parse_blocks( $content );
|
||||
$this->data->all_dynamic_blocks = self::wp_get_dynamic_block_names();
|
||||
$this->data->total_blocks = 0;
|
||||
$this->data->has_block_context = false;
|
||||
$this->data->has_block_timing = false;
|
||||
|
||||
if ( $this->data->post_has_blocks ) {
|
||||
$this->data->post_blocks = array_values( array_filter( array_map( array( $this, 'process_block' ), $this->data->post_blocks ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $block
|
||||
* @return mixed[]|null
|
||||
*/
|
||||
protected function process_block( array $block ) {
|
||||
$context = array_shift( $this->block_context );
|
||||
$timing = array_shift( $this->block_timing );
|
||||
|
||||
// Remove empty blocks caused by two consecutive line breaks in content
|
||||
if ( ! $block['blockName'] && ! trim( $block['innerHTML'] ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->data->total_blocks++;
|
||||
|
||||
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
|
||||
$dynamic = false;
|
||||
$callback = null;
|
||||
|
||||
if ( $block_type && $block_type->is_dynamic() ) {
|
||||
$dynamic = true;
|
||||
$callback = QM_Util::populate_callback( array(
|
||||
'function' => $block_type->render_callback,
|
||||
) );
|
||||
}
|
||||
|
||||
$timing = array_shift( $this->block_timing );
|
||||
|
||||
$block['dynamic'] = $dynamic;
|
||||
$block['callback'] = $callback;
|
||||
$block['innerHTML'] = trim( $block['innerHTML'] );
|
||||
$block['size'] = strlen( $block['innerHTML'] );
|
||||
|
||||
if ( $context ) {
|
||||
$block['context'] = $context;
|
||||
$this->data->has_block_context = true;
|
||||
}
|
||||
|
||||
if ( $timing ) {
|
||||
$block['timing'] = $timing->get_time();
|
||||
$this->data->has_block_timing = true;
|
||||
}
|
||||
|
||||
if ( ! empty( $block['innerBlocks'] ) ) {
|
||||
$block['innerBlocks'] = array_values( array_filter( array_map( array( $this, 'process_block' ), $block['innerBlocks'] ) ) );
|
||||
}
|
||||
|
||||
return $block;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected static function wp_block_editor_enabled() {
|
||||
return ( function_exists( 'parse_blocks' ) || function_exists( 'gutenberg_parse_blocks' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $content
|
||||
* @return bool
|
||||
*/
|
||||
protected static function wp_has_blocks( $content ) {
|
||||
if ( function_exists( 'has_blocks' ) ) {
|
||||
return has_blocks( $content );
|
||||
} elseif ( function_exists( 'gutenberg_has_blocks' ) ) {
|
||||
return gutenberg_has_blocks( $content );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $content
|
||||
* @return array<int, mixed>|null
|
||||
*/
|
||||
protected static function wp_parse_blocks( $content ) {
|
||||
if ( function_exists( 'parse_blocks' ) ) {
|
||||
return parse_blocks( $content );
|
||||
} elseif ( function_exists( 'gutenberg_parse_blocks' ) ) {
|
||||
return gutenberg_parse_blocks( $content );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>|null
|
||||
*/
|
||||
protected static function wp_get_dynamic_block_names() {
|
||||
if ( function_exists( 'get_dynamic_block_names' ) ) {
|
||||
return get_dynamic_block_names();
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, QM_Collector> $collectors
|
||||
* @param QueryMonitor $qm
|
||||
* @return array<string, QM_Collector>
|
||||
*/
|
||||
function register_qm_collector_block_editor( array $collectors, QueryMonitor $qm ) {
|
||||
$collectors['block_editor'] = new QM_Collector_Block_Editor();
|
||||
return $collectors;
|
||||
}
|
||||
|
||||
add_filter( 'qm/collectors', 'register_qm_collector_block_editor', 10, 2 );
|
130
wp-content/plugins/query-monitor/collectors/cache.php
Normal file
130
wp-content/plugins/query-monitor/collectors/cache.php
Normal file
@ -0,0 +1,130 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Object cache collector.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @extends QM_DataCollector<QM_Data_Cache>
|
||||
*/
|
||||
class QM_Collector_Cache extends QM_DataCollector {
|
||||
|
||||
public $id = 'cache';
|
||||
|
||||
public function get_storage(): QM_Data {
|
||||
return new QM_Data_Cache();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function process() {
|
||||
global $wp_object_cache;
|
||||
|
||||
$this->data->has_object_cache = (bool) wp_using_ext_object_cache();
|
||||
$this->data->cache_hit_percentage = 0;
|
||||
|
||||
if ( is_object( $wp_object_cache ) ) {
|
||||
$object_vars = get_object_vars( $wp_object_cache );
|
||||
|
||||
if ( array_key_exists( 'cache_hits', $object_vars ) ) {
|
||||
$this->data->stats['cache_hits'] = (int) $object_vars['cache_hits'];
|
||||
}
|
||||
|
||||
if ( array_key_exists( 'cache_misses', $object_vars ) ) {
|
||||
$this->data->stats['cache_misses'] = (int) $object_vars['cache_misses'];
|
||||
}
|
||||
|
||||
$stats = array();
|
||||
|
||||
if ( method_exists( $wp_object_cache, 'getStats' ) ) {
|
||||
$stats = $wp_object_cache->getStats();
|
||||
} elseif ( array_key_exists( 'stats', $object_vars ) && is_array( $object_vars['stats'] ) ) {
|
||||
$stats = $object_vars['stats'];
|
||||
} elseif ( function_exists( 'wp_cache_get_stats' ) ) {
|
||||
$stats = wp_cache_get_stats();
|
||||
}
|
||||
|
||||
if ( ! empty( $stats ) ) {
|
||||
if ( is_array( $stats ) && ! isset( $stats['get_hits'] ) && 1 === count( $stats ) ) {
|
||||
$first_server = reset( $stats );
|
||||
if ( isset( $first_server['get_hits'] ) ) {
|
||||
$stats = $first_server;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $stats as $key => $value ) {
|
||||
if ( ! is_scalar( $value ) ) {
|
||||
continue;
|
||||
}
|
||||
if ( ! is_string( $key ) ) {
|
||||
continue;
|
||||
}
|
||||
$this->data->stats[ $key ] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! isset( $this->data->stats['cache_hits'] ) ) {
|
||||
if ( isset( $this->data->stats['get_hits'] ) ) {
|
||||
$this->data->stats['cache_hits'] = (int) $this->data->stats['get_hits'];
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! isset( $this->data->stats['cache_misses'] ) ) {
|
||||
if ( isset( $this->data->stats['get_misses'] ) ) {
|
||||
$this->data->stats['cache_misses'] = (int) $this->data->stats['get_misses'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $this->data->stats['cache_hits'] ) ) {
|
||||
$total = $this->data->stats['cache_hits'];
|
||||
|
||||
if ( ! empty( $this->data->stats['cache_misses'] ) ) {
|
||||
$total += $this->data->stats['cache_misses'];
|
||||
}
|
||||
|
||||
$this->data->cache_hit_percentage = ( 100 / $total ) * $this->data->stats['cache_hits'];
|
||||
}
|
||||
|
||||
$this->data->display_hit_rate_warning = ( 100 === $this->data->cache_hit_percentage );
|
||||
|
||||
if ( function_exists( 'extension_loaded' ) ) {
|
||||
$this->data->object_cache_extensions = array_map( 'extension_loaded', array(
|
||||
'Afterburner' => 'afterburner',
|
||||
'APCu' => 'apcu',
|
||||
'Redis' => 'redis',
|
||||
'Relay' => 'relay',
|
||||
'Memcache' => 'memcache',
|
||||
'Memcached' => 'memcached',
|
||||
) );
|
||||
$this->data->opcode_cache_extensions = array_map( 'extension_loaded', array(
|
||||
'APC' => 'APC',
|
||||
'Zend OPcache' => 'Zend OPcache',
|
||||
) );
|
||||
} else {
|
||||
$this->data->object_cache_extensions = array();
|
||||
$this->data->opcode_cache_extensions = array();
|
||||
}
|
||||
|
||||
$this->data->has_opcode_cache = array_filter( $this->data->opcode_cache_extensions ) ? true : false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, QM_Collector> $collectors
|
||||
* @param QueryMonitor $qm
|
||||
* @return array<string, QM_Collector>
|
||||
*/
|
||||
function register_qm_collector_cache( array $collectors, QueryMonitor $qm ) {
|
||||
$collectors['cache'] = new QM_Collector_Cache();
|
||||
return $collectors;
|
||||
}
|
||||
|
||||
add_filter( 'qm/collectors', 'register_qm_collector_cache', 20, 2 );
|
311
wp-content/plugins/query-monitor/collectors/caps.php
Normal file
311
wp-content/plugins/query-monitor/collectors/caps.php
Normal file
@ -0,0 +1,311 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* User capability check collector.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @extends QM_DataCollector<QM_Data_Caps>
|
||||
* @phpstan-type CapCheck array{
|
||||
* args: list<mixed>,
|
||||
* filtered_trace: list<array<string, mixed>>,
|
||||
* component: QM_Component,
|
||||
* result: bool,
|
||||
* }
|
||||
*/
|
||||
class QM_Collector_Caps extends QM_DataCollector {
|
||||
|
||||
public $id = 'caps';
|
||||
|
||||
/**
|
||||
* @var array<int, array<string, mixed>>
|
||||
* @phpstan-var list<CapCheck>
|
||||
*/
|
||||
private $cap_checks = array();
|
||||
|
||||
public function get_storage(): QM_Data {
|
||||
return new QM_Data_Caps();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function set_up() {
|
||||
parent::set_up();
|
||||
|
||||
if ( ! self::enabled() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
add_filter( 'user_has_cap', array( $this, 'filter_user_has_cap' ), 9999, 3 );
|
||||
add_filter( 'map_meta_cap', array( $this, 'filter_map_meta_cap' ), 9999, 4 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function tear_down() {
|
||||
remove_filter( 'user_has_cap', array( $this, 'filter_user_has_cap' ), 9999 );
|
||||
remove_filter( 'map_meta_cap', array( $this, 'filter_map_meta_cap' ), 9999 );
|
||||
|
||||
parent::tear_down();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function enabled() {
|
||||
return ( defined( 'QM_ENABLE_CAPS_PANEL' ) && QM_ENABLE_CAPS_PANEL );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_concerned_actions() {
|
||||
return array(
|
||||
'wp_roles_init',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_concerned_filters() {
|
||||
return array(
|
||||
'map_meta_cap',
|
||||
'role_has_cap',
|
||||
'user_has_cap',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_concerned_options() {
|
||||
$blog_prefix = $GLOBALS['wpdb']->get_blog_prefix();
|
||||
|
||||
return array(
|
||||
"{$blog_prefix}user_roles",
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_concerned_constants() {
|
||||
return array(
|
||||
'ALLOW_UNFILTERED_UPLOADS',
|
||||
'DISALLOW_FILE_EDIT',
|
||||
'DISALLOW_UNFILTERED_HTML',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs user capability checks.
|
||||
*
|
||||
* This does not get called for Super Admins. See filter_map_meta_cap() below.
|
||||
*
|
||||
* @param array<string, bool> $user_caps Concerned user's capabilities.
|
||||
* @param array<int, string> $caps Required primitive capabilities for the requested capability.
|
||||
* @param array<int, mixed> $args {
|
||||
* Arguments that accompany the requested capability check.
|
||||
*
|
||||
* @type string $0 Requested capability.
|
||||
* @type int $1 Concerned user ID.
|
||||
* @type mixed ...$2 Optional second and further parameters.
|
||||
* }
|
||||
* @phpstan-param array{
|
||||
* 0: string,
|
||||
* 1: int,
|
||||
* } $args
|
||||
* @return array<string, bool> Concerned user's capabilities.
|
||||
*/
|
||||
public function filter_user_has_cap( array $user_caps, array $caps, array $args ) {
|
||||
$trace = new QM_Backtrace( array(
|
||||
'ignore_hook' => array(
|
||||
current_filter() => true,
|
||||
),
|
||||
'ignore_func' => array(
|
||||
'current_user_can' => true,
|
||||
'map_meta_cap' => true,
|
||||
'user_can' => true,
|
||||
),
|
||||
'ignore_method' => array(
|
||||
'WP_User' => array(
|
||||
'has_cap' => true,
|
||||
),
|
||||
),
|
||||
) );
|
||||
$result = true;
|
||||
|
||||
foreach ( $caps as $cap ) {
|
||||
if ( empty( $user_caps[ $cap ] ) ) {
|
||||
$result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$this->cap_checks[] = array(
|
||||
'args' => $args,
|
||||
'filtered_trace' => $trace->get_filtered_trace(),
|
||||
'component' => $trace->get_component(),
|
||||
'result' => $result,
|
||||
);
|
||||
|
||||
return $user_caps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs user capability checks for Super Admins on Multisite.
|
||||
*
|
||||
* This is needed because the `user_has_cap` filter doesn't fire for Super Admins.
|
||||
*
|
||||
* @param array<int, string> $required_caps Required primitive capabilities for the requested capability.
|
||||
* @param string $cap Capability or meta capability being checked.
|
||||
* @param int $user_id Concerned user ID.
|
||||
* @param mixed[] $args {
|
||||
* Arguments that accompany the requested capability check.
|
||||
*
|
||||
* @type mixed ...$0 Optional second and further parameters.
|
||||
* }
|
||||
* @return array<int, string> Required capabilities for the requested action.
|
||||
*/
|
||||
public function filter_map_meta_cap( array $required_caps, $cap, $user_id, array $args ) {
|
||||
if ( ! is_multisite() ) {
|
||||
return $required_caps;
|
||||
}
|
||||
|
||||
if ( ! is_super_admin( $user_id ) ) {
|
||||
return $required_caps;
|
||||
}
|
||||
|
||||
$trace = new QM_Backtrace( array(
|
||||
'ignore_hook' => array(
|
||||
current_filter() => true,
|
||||
),
|
||||
'ignore_func' => array(
|
||||
'current_user_can' => true,
|
||||
'map_meta_cap' => true,
|
||||
'user_can' => true,
|
||||
),
|
||||
'ignore_method' => array(
|
||||
'WP_User' => array(
|
||||
'has_cap' => true,
|
||||
),
|
||||
),
|
||||
) );
|
||||
$result = ( ! in_array( 'do_not_allow', $required_caps, true ) );
|
||||
|
||||
array_unshift( $args, $user_id );
|
||||
array_unshift( $args, $cap );
|
||||
|
||||
$this->cap_checks[] = array(
|
||||
'args' => $args,
|
||||
'filtered_trace' => $trace->get_filtered_trace(),
|
||||
'component' => $trace->get_component(),
|
||||
'result' => $result,
|
||||
);
|
||||
|
||||
return $required_caps;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function process() {
|
||||
if ( empty( $this->cap_checks ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$all_parts = array();
|
||||
$all_users = array();
|
||||
$components = array();
|
||||
$this->data->caps = array();
|
||||
|
||||
$this->cap_checks = array_values( array_filter( $this->cap_checks, array( $this, 'filter_remove_noise' ) ) );
|
||||
|
||||
if ( self::hide_qm() ) {
|
||||
$this->cap_checks = array_values( array_filter( $this->cap_checks, array( $this, 'filter_remove_qm' ) ) );
|
||||
}
|
||||
|
||||
foreach ( $this->cap_checks as $cap ) {
|
||||
$name = $cap['args'][0];
|
||||
|
||||
if ( ! is_string( $name ) ) {
|
||||
$name = '';
|
||||
}
|
||||
|
||||
$component = $cap['component'];
|
||||
$parts = array();
|
||||
$pieces = preg_split( '#[_/-]#', $name );
|
||||
|
||||
if ( is_array( $pieces ) ) {
|
||||
$parts = array_values( array_filter( $pieces ) );
|
||||
}
|
||||
|
||||
$capability = array_shift( $cap['args'] );
|
||||
$user_id = array_shift( $cap['args'] );
|
||||
|
||||
$cap['parts'] = $parts;
|
||||
$cap['name'] = $name;
|
||||
$cap['user'] = $user_id;
|
||||
|
||||
$this->data->caps[] = $cap;
|
||||
|
||||
$all_parts = array_merge( $all_parts, $parts );
|
||||
$all_users[] = (int) $user_id;
|
||||
$components[ $component->name ] = $component->name;
|
||||
}
|
||||
|
||||
$this->data->parts = array_values( array_unique( array_filter( $all_parts ) ) );
|
||||
$this->data->users = array_values( array_unique( array_filter( $all_users ) ) );
|
||||
$this->data->components = $components;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $cap
|
||||
* @phpstan-param CapCheck $cap
|
||||
* @return bool
|
||||
*/
|
||||
public function filter_remove_noise( array $cap ) {
|
||||
$trace = $cap['filtered_trace'];
|
||||
|
||||
$exclude_files = array(
|
||||
ABSPATH . 'wp-admin/menu.php',
|
||||
ABSPATH . 'wp-admin/includes/menu.php',
|
||||
);
|
||||
$exclude_functions = array(
|
||||
'_wp_menu_output',
|
||||
'wp_admin_bar_render',
|
||||
);
|
||||
|
||||
foreach ( $trace as $item ) {
|
||||
if ( isset( $item['file'] ) && in_array( $item['file'], $exclude_files, true ) ) {
|
||||
return false;
|
||||
}
|
||||
if ( isset( $item['function'] ) && in_array( $item['function'], $exclude_functions, true ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, QM_Collector> $collectors
|
||||
* @param QueryMonitor $qm
|
||||
* @return array<string, QM_Collector>
|
||||
*/
|
||||
function register_qm_collector_caps( array $collectors, QueryMonitor $qm ) {
|
||||
$collectors['caps'] = new QM_Collector_Caps();
|
||||
return $collectors;
|
||||
}
|
||||
|
||||
add_filter( 'qm/collectors', 'register_qm_collector_caps', 20, 2 );
|
126
wp-content/plugins/query-monitor/collectors/conditionals.php
Normal file
126
wp-content/plugins/query-monitor/collectors/conditionals.php
Normal file
@ -0,0 +1,126 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Template conditionals collector.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @extends QM_DataCollector<QM_Data_Conditionals>
|
||||
*/
|
||||
class QM_Collector_Conditionals extends QM_DataCollector {
|
||||
|
||||
public $id = 'conditionals';
|
||||
|
||||
public function get_storage(): QM_Data {
|
||||
return new QM_Data_Conditionals();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function process() {
|
||||
|
||||
/**
|
||||
* Allows users to filter the names of conditional functions that are exposed by QM.
|
||||
*
|
||||
* @since 2.7.0
|
||||
*
|
||||
* @param array<int, string> $conditionals The list of conditional function names.
|
||||
*/
|
||||
$conds = apply_filters( 'qm/collect/conditionals', array(
|
||||
'is_404',
|
||||
'is_admin',
|
||||
'is_archive',
|
||||
'is_attachment',
|
||||
'is_author',
|
||||
'is_blog_admin',
|
||||
'is_category',
|
||||
'is_comment_feed',
|
||||
'is_customize_preview',
|
||||
'is_date',
|
||||
'is_day',
|
||||
'is_embed',
|
||||
'is_favicon',
|
||||
'is_feed',
|
||||
'is_front_page',
|
||||
'is_home',
|
||||
'is_main_network',
|
||||
'is_main_site',
|
||||
'is_month',
|
||||
'is_network_admin',
|
||||
'is_page',
|
||||
'is_page_template',
|
||||
'is_paged',
|
||||
'is_post_type_archive',
|
||||
'is_preview',
|
||||
'is_privacy_policy',
|
||||
'is_robots',
|
||||
'is_rtl',
|
||||
'is_search',
|
||||
'is_single',
|
||||
'is_singular',
|
||||
'is_ssl',
|
||||
'is_sticky',
|
||||
'is_tag',
|
||||
'is_tax',
|
||||
'is_time',
|
||||
'is_trackback',
|
||||
'is_user_admin',
|
||||
'is_year',
|
||||
) );
|
||||
|
||||
/**
|
||||
* This filter is deprecated. Please use `qm/collect/conditionals` instead.
|
||||
*
|
||||
* @since 2.7.0
|
||||
*
|
||||
* @param array<int, string> $conditionals The list of conditional function names.
|
||||
*/
|
||||
$conds = apply_filters( 'query_monitor_conditionals', $conds );
|
||||
|
||||
$true = array();
|
||||
$false = array();
|
||||
$na = array();
|
||||
|
||||
foreach ( $conds as $cond ) {
|
||||
if ( function_exists( $cond ) ) {
|
||||
$id = null;
|
||||
if ( ( 'is_sticky' === $cond ) && ! get_post( $id ) ) {
|
||||
# Special case for is_sticky to prevent PHP notices
|
||||
$false[] = $cond;
|
||||
} elseif ( ! is_multisite() && in_array( $cond, array( 'is_main_network', 'is_main_site' ), true ) ) {
|
||||
# Special case for multisite conditionals to prevent them from being annoying on single site installations
|
||||
$na[] = $cond;
|
||||
} else {
|
||||
if ( call_user_func( $cond ) ) {
|
||||
$true[] = $cond;
|
||||
} else {
|
||||
$false[] = $cond;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$na[] = $cond;
|
||||
}
|
||||
}
|
||||
$this->data->conds = compact( 'true', 'false', 'na' );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, QM_Collector> $collectors
|
||||
* @param QueryMonitor $qm
|
||||
* @return array<string, QM_Collector>
|
||||
*/
|
||||
function register_qm_collector_conditionals( array $collectors, QueryMonitor $qm ) {
|
||||
$collectors['conditionals'] = new QM_Collector_Conditionals();
|
||||
return $collectors;
|
||||
}
|
||||
|
||||
add_filter( 'qm/collectors', 'register_qm_collector_conditionals', 10, 2 );
|
54
wp-content/plugins/query-monitor/collectors/db_callers.php
Normal file
54
wp-content/plugins/query-monitor/collectors/db_callers.php
Normal file
@ -0,0 +1,54 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Database query calling function collector.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @extends QM_DataCollector<QM_Data_DB_Callers>
|
||||
*/
|
||||
class QM_Collector_DB_Callers extends QM_DataCollector {
|
||||
|
||||
public $id = 'db_callers';
|
||||
|
||||
public function get_storage(): QM_Data {
|
||||
return new QM_Data_DB_Callers();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function process() {
|
||||
/** @var QM_Collector_DB_Queries|null $dbq */
|
||||
$dbq = QM_Collectors::get( 'db_queries' );
|
||||
|
||||
if ( $dbq ) {
|
||||
/** @var QM_Data_DB_Queries $dbq_data */
|
||||
$dbq_data = $dbq->get_data();
|
||||
|
||||
$this->data->times = $dbq_data->times;
|
||||
QM_Util::rsort( $this->data->times, 'ltime' );
|
||||
|
||||
$this->data->types = $dbq_data->types;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, QM_Collector> $collectors
|
||||
* @param QueryMonitor $qm
|
||||
* @return array<string, QM_Collector>
|
||||
*/
|
||||
function register_qm_collector_db_callers( array $collectors, QueryMonitor $qm ) {
|
||||
$collectors['db_callers'] = new QM_Collector_DB_Callers();
|
||||
return $collectors;
|
||||
}
|
||||
|
||||
add_filter( 'qm/collectors', 'register_qm_collector_db_callers', 20, 2 );
|
@ -0,0 +1,54 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Database query calling component collector.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @extends QM_DataCollector<QM_Data_DB_Components>
|
||||
*/
|
||||
class QM_Collector_DB_Components extends QM_DataCollector {
|
||||
|
||||
public $id = 'db_components';
|
||||
|
||||
public function get_storage(): QM_Data {
|
||||
return new QM_Data_DB_Components();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function process() {
|
||||
/** @var QM_Collector_DB_Queries|null $dbq */
|
||||
$dbq = QM_Collectors::get( 'db_queries' );
|
||||
|
||||
if ( $dbq ) {
|
||||
/** @var QM_Data_DB_Queries $dbq_data */
|
||||
$dbq_data = $dbq->get_data();
|
||||
|
||||
$this->data->times = $dbq_data->component_times;
|
||||
QM_Util::rsort( $this->data->times, 'ltime' );
|
||||
|
||||
$this->data->types = $dbq_data->types;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, QM_Collector> $collectors
|
||||
* @param QueryMonitor $qm
|
||||
* @return array<string, QM_Collector>
|
||||
*/
|
||||
function register_qm_collector_db_components( array $collectors, QueryMonitor $qm ) {
|
||||
$collectors['db_components'] = new QM_Collector_DB_Components();
|
||||
return $collectors;
|
||||
}
|
||||
|
||||
add_filter( 'qm/collectors', 'register_qm_collector_db_components', 20, 2 );
|
131
wp-content/plugins/query-monitor/collectors/db_dupes.php
Normal file
131
wp-content/plugins/query-monitor/collectors/db_dupes.php
Normal file
@ -0,0 +1,131 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Duplicate database query collector.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @extends QM_DataCollector<QM_Data_DB_Dupes>
|
||||
*/
|
||||
class QM_Collector_DB_Dupes extends QM_DataCollector {
|
||||
|
||||
public $id = 'db_dupes';
|
||||
|
||||
public function get_storage(): QM_Data {
|
||||
return new QM_Data_DB_Dupes();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function process() {
|
||||
/** @var QM_Collector_DB_Queries|null $dbq */
|
||||
$dbq = QM_Collectors::get( 'db_queries' );
|
||||
|
||||
if ( ! $dbq ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var QM_Data_DB_Queries $dbq_data */
|
||||
$dbq_data = $dbq->get_data();
|
||||
|
||||
if ( empty( $dbq_data->dupes ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Filter out SQL queries that do not have dupes
|
||||
$this->data->dupes = array_filter( $dbq_data->dupes, array( $this, 'filter_dupe_items' ) );
|
||||
|
||||
// Ignore dupes from `WP_Query->set_found_posts()`
|
||||
unset( $this->data->dupes['SELECT FOUND_ROWS()'] );
|
||||
|
||||
$stacks = array();
|
||||
$callers = array();
|
||||
$components = array();
|
||||
$times = array();
|
||||
|
||||
// Loop over all SQL queries that have dupes
|
||||
foreach ( $this->data->dupes as $sql => $query_ids ) {
|
||||
|
||||
// Loop over each query
|
||||
foreach ( $query_ids as $query_id ) {
|
||||
|
||||
if ( isset( $dbq_data->wpdb->rows[ $query_id ]['trace'] ) ) {
|
||||
/** @var QM_Backtrace */
|
||||
$trace = $dbq_data->wpdb->rows[ $query_id ]['trace'];
|
||||
$stack = array_column( $trace->get_filtered_trace(), 'id' );
|
||||
$component = $trace->get_component();
|
||||
|
||||
// Populate the component counts for this query
|
||||
if ( isset( $components[ $sql ][ $component->name ] ) ) {
|
||||
$components[ $sql ][ $component->name ]++;
|
||||
} else {
|
||||
$components[ $sql ][ $component->name ] = 1;
|
||||
}
|
||||
} else {
|
||||
/** @var array<int, string> */
|
||||
$stack = $dbq_data->wpdb->rows[ $query_id ]['stack'];
|
||||
}
|
||||
|
||||
// Populate the caller counts for this query
|
||||
if ( isset( $callers[ $sql ][ $stack[0] ] ) ) {
|
||||
$callers[ $sql ][ $stack[0] ]++;
|
||||
} else {
|
||||
$callers[ $sql ][ $stack[0] ] = 1;
|
||||
}
|
||||
|
||||
// Populate the stack for this query
|
||||
$stacks[ $sql ][] = $stack;
|
||||
|
||||
// Populate the time for this query
|
||||
if ( isset( $times[ $sql ] ) ) {
|
||||
$times[ $sql ] += $dbq->data->wpdb->rows[ $query_id ]['ltime'];
|
||||
} else {
|
||||
$times[ $sql ] = $dbq->data->wpdb->rows[ $query_id ]['ltime'];
|
||||
}
|
||||
}
|
||||
|
||||
// Get the callers which are common to all stacks for this query
|
||||
$common = call_user_func_array( 'array_intersect', $stacks[ $sql ] );
|
||||
|
||||
// Remove callers which are common to all stacks for this query
|
||||
foreach ( $stacks[ $sql ] as $i => $stack ) {
|
||||
$stacks[ $sql ][ $i ] = array_values( array_diff( $stack, $common ) );
|
||||
|
||||
// No uncommon callers within the stack? Just use the topmost caller.
|
||||
if ( empty( $stacks[ $sql ][ $i ] ) ) {
|
||||
$stacks[ $sql ][ $i ] = array_keys( $callers[ $sql ] );
|
||||
}
|
||||
}
|
||||
|
||||
// Wave a magic wand
|
||||
$sources[ $sql ] = array_count_values( array_column( $stacks[ $sql ], 0 ) );
|
||||
|
||||
}
|
||||
|
||||
if ( ! empty( $sources ) ) {
|
||||
$this->data->dupe_sources = $sources;
|
||||
$this->data->dupe_callers = $callers;
|
||||
$this->data->dupe_components = $components;
|
||||
$this->data->dupe_times = $times;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, QM_Collector> $collectors
|
||||
* @param QueryMonitor $qm
|
||||
* @return array<string, QM_Collector>
|
||||
*/
|
||||
function register_qm_collector_db_dupes( array $collectors, QueryMonitor $qm ) {
|
||||
$collectors['db_dupes'] = new QM_Collector_DB_Dupes();
|
||||
return $collectors;
|
||||
}
|
||||
|
||||
add_filter( 'qm/collectors', 'register_qm_collector_db_dupes', 25, 2 );
|
286
wp-content/plugins/query-monitor/collectors/db_queries.php
Normal file
286
wp-content/plugins/query-monitor/collectors/db_queries.php
Normal file
@ -0,0 +1,286 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Database query collector.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( ! defined( 'SAVEQUERIES' ) ) {
|
||||
define( 'SAVEQUERIES', true );
|
||||
}
|
||||
if ( ! defined( 'QM_DB_EXPENSIVE' ) ) {
|
||||
define( 'QM_DB_EXPENSIVE', 0.05 );
|
||||
}
|
||||
|
||||
if ( SAVEQUERIES && property_exists( $GLOBALS['wpdb'], 'save_queries' ) ) {
|
||||
$GLOBALS['wpdb']->save_queries = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @extends QM_DataCollector<QM_Data_DB_Queries>
|
||||
*/
|
||||
class QM_Collector_DB_Queries extends QM_DataCollector {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $id = 'db_queries';
|
||||
|
||||
/**
|
||||
* @var wpdb
|
||||
*/
|
||||
public $wpdb;
|
||||
|
||||
public function get_storage(): QM_Data {
|
||||
return new QM_Data_DB_Queries();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]|false
|
||||
*/
|
||||
public function get_errors() {
|
||||
if ( ! empty( $this->data->errors ) ) {
|
||||
return $this->data->errors;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]|false
|
||||
*/
|
||||
public function get_expensive() {
|
||||
if ( ! empty( $this->data->expensive ) ) {
|
||||
return $this->data->expensive;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $row
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_expensive( array $row ) {
|
||||
return $row['ltime'] > QM_DB_EXPENSIVE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function process() {
|
||||
$this->data->total_qs = 0;
|
||||
$this->data->total_time = 0;
|
||||
$this->data->errors = array();
|
||||
$this->process_db_object();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $caller
|
||||
* @param float $ltime
|
||||
* @param string $type
|
||||
* @return void
|
||||
*/
|
||||
protected function log_caller( $caller, $ltime, $type ) {
|
||||
|
||||
if ( ! isset( $this->data->times[ $caller ] ) ) {
|
||||
$this->data->times[ $caller ] = array(
|
||||
'caller' => $caller,
|
||||
'ltime' => 0,
|
||||
'types' => array(),
|
||||
);
|
||||
}
|
||||
|
||||
$this->data->times[ $caller ]['ltime'] += $ltime;
|
||||
|
||||
if ( isset( $this->data->times[ $caller ]['types'][ $type ] ) ) {
|
||||
$this->data->times[ $caller ]['types'][ $type ]++;
|
||||
} else {
|
||||
$this->data->times[ $caller ]['types'][ $type ] = 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function process_db_object() {
|
||||
global $wp_the_query, $wpdb;
|
||||
|
||||
$this->wpdb = $wpdb;
|
||||
|
||||
// With SAVEQUERIES defined as false, `wpdb::queries` is empty but `wpdb::num_queries` is not.
|
||||
if ( empty( $wpdb->queries ) ) {
|
||||
$this->data->total_qs += $wpdb->num_queries;
|
||||
return;
|
||||
}
|
||||
|
||||
$rows = array();
|
||||
$types = array();
|
||||
$total_time = 0;
|
||||
$has_result = false;
|
||||
$has_trace = false;
|
||||
$i = 0;
|
||||
$request = trim( $wp_the_query->request ?: '' );
|
||||
|
||||
if ( method_exists( $wpdb, 'remove_placeholder_escape' ) ) {
|
||||
$request = $wpdb->remove_placeholder_escape( $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-var array{
|
||||
* 0: string,
|
||||
* 1: float,
|
||||
* 2: string,
|
||||
* trace?: QM_Backtrace,
|
||||
* result?: int|bool|WP_Error,
|
||||
* }|array{
|
||||
* query: string,
|
||||
* elapsed: float,
|
||||
* debug: string,
|
||||
* } $query
|
||||
*/
|
||||
foreach ( $wpdb->queries as $query ) {
|
||||
$has_trace = false;
|
||||
$has_result = false;
|
||||
$callers = array();
|
||||
|
||||
if ( isset( $query['query'], $query['elapsed'], $query['debug'] ) ) {
|
||||
// WordPress.com VIP.
|
||||
$sql = $query['query'];
|
||||
$ltime = $query['elapsed'];
|
||||
$stack = $query['debug'];
|
||||
} elseif ( isset( $query[0], $query[1], $query[2] ) ) {
|
||||
// Standard WP.
|
||||
$sql = $query[0];
|
||||
$ltime = $query[1];
|
||||
$stack = $query[2];
|
||||
|
||||
// Query Monitor db.php drop-in.
|
||||
$has_trace = isset( $query['trace'] );
|
||||
$has_result = isset( $query['result'] );
|
||||
} else {
|
||||
// ¯\_(ツ)_/¯
|
||||
continue;
|
||||
}
|
||||
|
||||
// @TODO: decide what I want to do with this:
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
if ( false !== strpos( $stack, 'wp_admin_bar' ) && ! isset( $_REQUEST['qm_display_admin_bar'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$result = $query['result'] ?? null;
|
||||
$total_time += $ltime;
|
||||
|
||||
if ( isset( $query['trace'] ) ) {
|
||||
|
||||
$trace = $query['trace'];
|
||||
$component = $query['trace']->get_component();
|
||||
$caller = $query['trace']->get_caller();
|
||||
$caller_name = $caller['display'] ?? 'Unknown';
|
||||
$caller = $caller['display'] ?? 'Unknown';
|
||||
|
||||
} else {
|
||||
|
||||
$trace = null;
|
||||
$component = null;
|
||||
$callers = array_reverse( explode( ',', $stack ) );
|
||||
$callers = array_map( 'trim', $callers );
|
||||
$callers = QM_Backtrace::get_filtered_stack( $callers );
|
||||
$caller = reset( $callers );
|
||||
$caller_name = $caller;
|
||||
|
||||
}
|
||||
|
||||
$sql = trim( $sql );
|
||||
$type = QM_Util::get_query_type( $sql );
|
||||
|
||||
$this->log_type( $type );
|
||||
$this->log_caller( $caller_name, $ltime, $type );
|
||||
|
||||
$this->maybe_log_dupe( $sql, $i );
|
||||
|
||||
if ( $component ) {
|
||||
$this->log_component( $component, $ltime, $type );
|
||||
}
|
||||
|
||||
if ( ! isset( $types[ $type ]['total'] ) ) {
|
||||
$types[ $type ]['total'] = 1;
|
||||
} else {
|
||||
$types[ $type ]['total']++;
|
||||
}
|
||||
|
||||
if ( ! isset( $types[ $type ]['callers'][ $caller ] ) ) {
|
||||
$types[ $type ]['callers'][ $caller ] = 1;
|
||||
} else {
|
||||
$types[ $type ]['callers'][ $caller ]++;
|
||||
}
|
||||
|
||||
$is_main_query = ( $request === $sql && ( false !== strpos( $stack, ' WP->main,' ) ) );
|
||||
|
||||
$row = compact( 'caller', 'caller_name', 'sql', 'ltime', 'result', 'type', 'component', 'trace', 'is_main_query' );
|
||||
|
||||
if ( ! isset( $trace ) ) {
|
||||
$row['stack'] = $callers;
|
||||
}
|
||||
|
||||
// @TODO these should store a reference ($i) instead of the whole row
|
||||
if ( $result instanceof WP_Error ) {
|
||||
$this->data->errors[] = $row;
|
||||
}
|
||||
|
||||
// @TODO these should store a reference ($i) instead of the whole row
|
||||
if ( self::is_expensive( $row ) ) {
|
||||
$this->data->expensive[] = $row;
|
||||
}
|
||||
|
||||
$rows[ $i ] = $row;
|
||||
$i++;
|
||||
|
||||
}
|
||||
|
||||
$total_qs = count( $rows );
|
||||
|
||||
$this->data->total_qs += $total_qs;
|
||||
$this->data->total_time += $total_time;
|
||||
|
||||
$has_main_query = wp_list_filter( $rows, array(
|
||||
'is_main_query' => true,
|
||||
) );
|
||||
|
||||
# @TODO put errors in here too:
|
||||
# @TODO proper class instead of (object)
|
||||
$this->data->wpdb = (object) compact( 'rows', 'types', 'has_result', 'has_trace', 'total_time', 'total_qs', 'has_main_query' );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sql
|
||||
* @param int $i
|
||||
* @return void
|
||||
*/
|
||||
protected function maybe_log_dupe( $sql, $i ) {
|
||||
$sql = str_replace( array( "\r\n", "\r", "\n" ), ' ', $sql );
|
||||
$sql = str_replace( array( "\t", '`' ), '', $sql );
|
||||
$sql = preg_replace( '/ +/', ' ', $sql );
|
||||
$sql = trim( $sql );
|
||||
$sql = rtrim( $sql, ';' );
|
||||
|
||||
$this->data->dupes[ $sql ][] = $i;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, QM_Collector> $collectors
|
||||
* @param QueryMonitor $qm
|
||||
* @return array<string, QM_Collector>
|
||||
*/
|
||||
function register_qm_collector_db_queries( array $collectors, QueryMonitor $qm ) {
|
||||
$collectors['db_queries'] = new QM_Collector_DB_Queries();
|
||||
return $collectors;
|
||||
}
|
||||
|
||||
add_filter( 'qm/collectors', 'register_qm_collector_db_queries', 10, 2 );
|
139
wp-content/plugins/query-monitor/collectors/debug_bar.php
Normal file
139
wp-content/plugins/query-monitor/collectors/debug_bar.php
Normal file
@ -0,0 +1,139 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Mock 'Debug Bar' data collector.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
final class QM_Collector_Debug_Bar extends QM_Collector {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $id = 'debug_bar';
|
||||
|
||||
/**
|
||||
* @var Debug_Bar_Panel|null
|
||||
*/
|
||||
private $panel = null;
|
||||
|
||||
/**
|
||||
* @param Debug_Bar_Panel $panel
|
||||
* @return void
|
||||
*/
|
||||
public function set_panel( Debug_Bar_Panel $panel ) {
|
||||
$this->panel = $panel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Debug_Bar_Panel|null
|
||||
*/
|
||||
public function get_panel() {
|
||||
return $this->panel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function process() {
|
||||
$this->get_panel()->prerender();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function is_visible() {
|
||||
return $this->get_panel()->is_visible();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function render() {
|
||||
$this->get_panel()->render();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
function register_qm_collectors_debug_bar() {
|
||||
|
||||
global $debug_bar;
|
||||
|
||||
if ( class_exists( 'Debug_Bar', false ) || qm_debug_bar_being_activated() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$collectors = QM_Collectors::init();
|
||||
|
||||
$debug_bar = new Debug_Bar();
|
||||
$redundant = array(
|
||||
'debug_bar_actions_addon_panel', // Debug Bar Actions and Filters Addon
|
||||
'debug_bar_remote_requests_panel', // Debug Bar Remote Requests
|
||||
'debug_bar_screen_info_panel', // Debug Bar Screen Info
|
||||
'ps_listdeps_debug_bar_panel', // Debug Bar List Script & Style Dependencies
|
||||
);
|
||||
|
||||
foreach ( $debug_bar->panels as $panel ) {
|
||||
$panel_id = strtolower( sanitize_html_class( get_class( $panel ) ) );
|
||||
|
||||
if ( in_array( $panel_id, $redundant, true ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$collector = new QM_Collector_Debug_Bar();
|
||||
$collector->set_id( "debug_bar_{$panel_id}" );
|
||||
$collector->set_panel( $panel );
|
||||
|
||||
$collectors->add( $collector );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
function qm_debug_bar_being_activated() {
|
||||
// phpcs:disable
|
||||
|
||||
if ( ! is_admin() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! isset( $_REQUEST['action'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( isset( $_GET['action'] ) ) {
|
||||
|
||||
if ( ! isset( $_GET['plugin'] ) || ! isset( $_GET['_wpnonce'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( 'activate' === $_GET['action'] && false !== strpos( wp_unslash( $_GET['plugin'] ), 'debug-bar.php' ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
} elseif ( isset( $_POST['action'] ) ) {
|
||||
|
||||
if ( ! isset( $_POST['checked'] ) || ! is_array( $_POST['checked'] ) || ! isset( $_POST['_wpnonce'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( 'activate-selected' === wp_unslash( $_POST['action'] ) && in_array( 'debug-bar/debug-bar.php', wp_unslash( $_POST['checked'] ), true ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
// phpcs:enable
|
||||
}
|
||||
|
||||
add_action( 'init', 'register_qm_collectors_debug_bar' );
|
341
wp-content/plugins/query-monitor/collectors/doing_it_wrong.php
Normal file
341
wp-content/plugins/query-monitor/collectors/doing_it_wrong.php
Normal file
@ -0,0 +1,341 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Doing it Wrong collector.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @extends QM_DataCollector<QM_Data_Doing_It_Wrong>
|
||||
*/
|
||||
class QM_Collector_Doing_It_Wrong extends QM_DataCollector {
|
||||
|
||||
public $id = 'doing_it_wrong';
|
||||
|
||||
public function get_storage(): QM_Data {
|
||||
return new QM_Data_Doing_It_Wrong();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function set_up() {
|
||||
parent::set_up();
|
||||
|
||||
add_action( 'doing_it_wrong_run', array( $this, 'action_doing_it_wrong_run' ), 10, 3 );
|
||||
add_action( 'deprecated_function_run', array( $this, 'action_deprecated_function_run' ), 10, 3 );
|
||||
add_action( 'deprecated_constructor_run', array( $this, 'action_deprecated_constructor_run' ), 10, 3 );
|
||||
add_action( 'deprecated_file_included', array( $this, 'action_deprecated_file_included' ), 10, 4 );
|
||||
add_action( 'deprecated_argument_run', array( $this, 'action_deprecated_argument_run' ), 10, 3 );
|
||||
add_action( 'deprecated_hook_run', array( $this, 'action_deprecated_hook_run' ), 10, 4 );
|
||||
|
||||
add_filter( 'deprecated_function_trigger_error', array( $this, 'maybe_prevent_error' ), 999 );
|
||||
add_filter( 'deprecated_constructor_trigger_error', array( $this, 'maybe_prevent_error' ), 999 );
|
||||
add_filter( 'deprecated_file_trigger_error', array( $this, 'maybe_prevent_error' ), 999 );
|
||||
add_filter( 'deprecated_argument_trigger_error', array( $this, 'maybe_prevent_error' ), 999 );
|
||||
add_filter( 'deprecated_hook_trigger_error', array( $this, 'maybe_prevent_error' ), 999 );
|
||||
add_filter( 'doing_it_wrong_trigger_error', array( $this, 'maybe_prevent_error' ), 999 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function tear_down() {
|
||||
parent::tear_down();
|
||||
|
||||
remove_action( 'doing_it_wrong_run', array( $this, 'action_doing_it_wrong_run' ) );
|
||||
remove_action( 'deprecated_function_run', array( $this, 'action_deprecated_function_run' ) );
|
||||
remove_action( 'deprecated_constructor_run', array( $this, 'action_deprecated_constructor_run' ) );
|
||||
remove_action( 'deprecated_file_included', array( $this, 'action_deprecated_file_included' ) );
|
||||
remove_action( 'deprecated_argument_run', array( $this, 'action_deprecated_argument_run' ) );
|
||||
remove_action( 'deprecated_hook_run', array( $this, 'action_deprecated_hook_run' ) );
|
||||
|
||||
remove_filter( 'deprecated_function_trigger_error', array( $this, 'maybe_prevent_error' ) );
|
||||
remove_filter( 'deprecated_constructor_trigger_error', array( $this, 'maybe_prevent_error' ) );
|
||||
remove_filter( 'deprecated_file_trigger_error', array( $this, 'maybe_prevent_error' ) );
|
||||
remove_filter( 'deprecated_argument_trigger_error', array( $this, 'maybe_prevent_error' ) );
|
||||
remove_filter( 'deprecated_hook_trigger_error', array( $this, 'maybe_prevent_error' ) );
|
||||
remove_filter( 'doing_it_wrong_trigger_error', array( $this, 'maybe_prevent_error' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents the PHP error (notice or deprecated) from being triggered for doing it wrong calls when the
|
||||
* current user can view Query Monitor output.
|
||||
*
|
||||
* @param bool $trigger
|
||||
* @return bool
|
||||
*/
|
||||
public function maybe_prevent_error( $trigger ) {
|
||||
if ( function_exists( 'wp_get_current_user' ) && current_user_can( 'view_query_monitor' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $trigger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_concerned_actions() {
|
||||
return array(
|
||||
'doing_it_wrong_run',
|
||||
'deprecated_function_run',
|
||||
'deprecated_constructor_run',
|
||||
'deprecated_file_included',
|
||||
'deprecated_argument_run',
|
||||
'deprecated_hook_run',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_concerned_filters() {
|
||||
return array(
|
||||
'deprecated_function_trigger_error',
|
||||
'deprecated_constructor_trigger_error',
|
||||
'deprecated_file_trigger_error',
|
||||
'deprecated_argument_trigger_error',
|
||||
'deprecated_hook_trigger_error',
|
||||
'doing_it_wrong_trigger_error',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $function_name
|
||||
* @param string $message
|
||||
* @param string $version
|
||||
* @return void
|
||||
*/
|
||||
public function action_doing_it_wrong_run( $function_name, $message, $version ) {
|
||||
$trace = new QM_Backtrace( array(
|
||||
'ignore_hook' => array(
|
||||
current_action() => true,
|
||||
),
|
||||
) );
|
||||
|
||||
if ( $version ) {
|
||||
/* translators: %s: Version number. */
|
||||
$version = sprintf( __( '(This message was added in version %s.)', 'query-monitor' ), $version );
|
||||
}
|
||||
|
||||
$this->data->actions[] = array(
|
||||
'hook' => 'doing_it_wrong_run',
|
||||
'filtered_trace' => $trace->get_filtered_trace(),
|
||||
'component' => $trace->get_component(),
|
||||
'message' => sprintf(
|
||||
/* translators: Developer debugging message. 1: PHP function name, 2: Explanatory message, 3: WordPress version number. */
|
||||
__( 'Function %1$s was called incorrectly. %2$s %3$s', 'query-monitor' ),
|
||||
$function_name,
|
||||
$message,
|
||||
$version
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $function_name
|
||||
* @param string $replacement
|
||||
* @param string $version
|
||||
* @return void
|
||||
*/
|
||||
public function action_deprecated_function_run( $function_name, $replacement, $version ) {
|
||||
$trace = new QM_Backtrace( array(
|
||||
'ignore_hook' => array(
|
||||
current_action() => true,
|
||||
),
|
||||
) );
|
||||
|
||||
$message = sprintf(
|
||||
/* translators: 1: PHP function name, 2: Version number. */
|
||||
__( 'Function %1$s is deprecated since version %2$s with no alternative available.', 'query-monitor' ),
|
||||
$function_name,
|
||||
$version
|
||||
);
|
||||
|
||||
if ( $replacement ) {
|
||||
$message = sprintf(
|
||||
/* translators: 1: PHP function name, 2: Version number, 3: Alternative function name. */
|
||||
__( 'Function %1$s is deprecated since version %2$s! Use %3$s instead.', 'query-monitor' ),
|
||||
$function_name,
|
||||
$version,
|
||||
$replacement
|
||||
);
|
||||
}
|
||||
|
||||
$this->data->actions[] = array(
|
||||
'hook' => 'deprecated_function_run',
|
||||
'filtered_trace' => $trace->get_filtered_trace(),
|
||||
'component' => $trace->get_component(),
|
||||
'message' => $message,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $class_name
|
||||
* @param string $version
|
||||
* @param string $parent_class
|
||||
* @return void
|
||||
*/
|
||||
public function action_deprecated_constructor_run( $class_name, $version, $parent_class ) {
|
||||
$trace = new QM_Backtrace( array(
|
||||
'ignore_hook' => array(
|
||||
current_action() => true,
|
||||
),
|
||||
) );
|
||||
|
||||
$message = sprintf(
|
||||
/* translators: 1: PHP class name, 2: Version number, 3: __construct() method. */
|
||||
__( 'The called constructor method for %1$s class is deprecated since version %2$s! Use %3$s instead.', 'query-monitor' ),
|
||||
$class_name,
|
||||
$version,
|
||||
'<code>__construct()</code>'
|
||||
);
|
||||
|
||||
if ( $parent_class ) {
|
||||
$message = sprintf(
|
||||
/* translators: 1: PHP class name, 2: PHP parent class name, 3: Version number, 4: __construct() method. */
|
||||
__( 'The called constructor method for %1$s class in %2$s is deprecated since version %3$s! Use %4$s instead.', 'query-monitor' ),
|
||||
$class_name,
|
||||
$parent_class,
|
||||
$version,
|
||||
'<code>__construct()</code>'
|
||||
);
|
||||
}
|
||||
|
||||
$this->data->actions[] = array(
|
||||
'hook' => 'deprecated_constructor_run',
|
||||
'filtered_trace' => $trace->get_filtered_trace(),
|
||||
'component' => $trace->get_component(),
|
||||
'message' => $message,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
* @param string $replacement
|
||||
* @param string $version
|
||||
* @param string $message
|
||||
* @return void
|
||||
*/
|
||||
public function action_deprecated_file_included( $file, $replacement, $version, $message ) {
|
||||
$trace = new QM_Backtrace( array(
|
||||
'ignore_hook' => array(
|
||||
current_action() => true,
|
||||
),
|
||||
) );
|
||||
|
||||
if ( $replacement ) {
|
||||
$message = sprintf(
|
||||
/* translators: 1: PHP file name, 2: Version number, 3: Alternative file name, 4: Optional message regarding the change. */
|
||||
__( 'File %1$s is deprecated since version %2$s! Use %3$s instead. %4$s', 'query-monitor' ),
|
||||
$file,
|
||||
$version,
|
||||
$replacement,
|
||||
$message
|
||||
);
|
||||
} else {
|
||||
$message = sprintf(
|
||||
/* translators: 1: PHP file name, 2: Version number, 3: Optional message regarding the change. */
|
||||
__( 'File %1$s is deprecated since version %2$s with no alternative available. %3$s', 'query-monitor' ),
|
||||
$file,
|
||||
$version,
|
||||
$message
|
||||
);
|
||||
}
|
||||
|
||||
$this->data->actions[] = array(
|
||||
'hook' => 'deprecated_file_included',
|
||||
'filtered_trace' => $trace->get_filtered_trace(),
|
||||
'component' => $trace->get_component(),
|
||||
'message' => $message,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $function_name
|
||||
* @param string $message
|
||||
* @param string $version
|
||||
* @return void
|
||||
*/
|
||||
public function action_deprecated_argument_run( $function_name, $message, $version ) {
|
||||
$trace = new QM_Backtrace( array(
|
||||
'ignore_hook' => array(
|
||||
current_action() => true,
|
||||
),
|
||||
) );
|
||||
|
||||
if ( $message ) {
|
||||
$message = sprintf(
|
||||
/* translators: 1: PHP function name, 2: Version number, 3: Optional message regarding the change. */
|
||||
__( 'Function %1$s was called with an argument that is deprecated since version %2$s! %3$s', 'query-monitor' ),
|
||||
$function_name,
|
||||
$version,
|
||||
$message
|
||||
);
|
||||
} else {
|
||||
$message = sprintf(
|
||||
/* translators: 1: PHP function name, 2: Version number. */
|
||||
__( 'Function %1$s was called with an argument that is deprecated since version %2$s with no alternative available.', 'query-monitor' ),
|
||||
$function_name,
|
||||
$version
|
||||
);
|
||||
}
|
||||
|
||||
$this->data->actions[] = array(
|
||||
'hook' => 'deprecated_argument_run',
|
||||
'filtered_trace' => $trace->get_filtered_trace(),
|
||||
'component' => $trace->get_component(),
|
||||
'message' => $message,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $hook
|
||||
* @param string $replacement
|
||||
* @param string $version
|
||||
* @param string $message
|
||||
* @return void
|
||||
*/
|
||||
public function action_deprecated_hook_run( $hook, $replacement, $version, $message ) {
|
||||
$trace = new QM_Backtrace( array(
|
||||
'ignore_hook' => array(
|
||||
current_action() => true,
|
||||
),
|
||||
) );
|
||||
|
||||
if ( $replacement ) {
|
||||
$message = sprintf(
|
||||
/* translators: 1: WordPress hook name, 2: Version number, 3: Alternative hook name, 4: Optional message regarding the change. */
|
||||
__( 'Hook %1$s is deprecated since version %2$s! Use %3$s instead. %4$s', 'query-monitor' ),
|
||||
$hook,
|
||||
$version,
|
||||
$replacement,
|
||||
$message
|
||||
);
|
||||
} else {
|
||||
$message = sprintf(
|
||||
/* translators: 1: WordPress hook name, 2: Version number, 3: Optional message regarding the change. */
|
||||
__( 'Hook %1$s is deprecated since version %2$s with no alternative available. %3$s', 'query-monitor' ),
|
||||
$hook,
|
||||
$version,
|
||||
$message
|
||||
);
|
||||
}
|
||||
|
||||
$this->data->actions[] = array(
|
||||
'hook' => 'deprecated_hook_run',
|
||||
'filtered_trace' => $trace->get_filtered_trace(),
|
||||
'component' => $trace->get_component(),
|
||||
'message' => $message,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Load early to catch early actions
|
||||
QM_Collectors::add( new QM_Collector_Doing_It_Wrong() );
|
338
wp-content/plugins/query-monitor/collectors/environment.php
Normal file
338
wp-content/plugins/query-monitor/collectors/environment.php
Normal file
@ -0,0 +1,338 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Environment data collector.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @extends QM_DataCollector<QM_Data_Environment>
|
||||
*/
|
||||
class QM_Collector_Environment extends QM_DataCollector {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $id = 'environment';
|
||||
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $php_vars = array(
|
||||
'max_execution_time',
|
||||
'memory_limit',
|
||||
'upload_max_filesize',
|
||||
'post_max_size',
|
||||
'display_errors',
|
||||
'log_errors',
|
||||
);
|
||||
|
||||
public function get_storage(): QM_Data {
|
||||
return new QM_Data_Environment();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $error_reporting
|
||||
* @return array<string, bool>
|
||||
*/
|
||||
protected static function get_error_levels( $error_reporting ) {
|
||||
$levels = array(
|
||||
'E_ERROR' => false,
|
||||
'E_WARNING' => false,
|
||||
'E_PARSE' => false,
|
||||
'E_NOTICE' => false,
|
||||
'E_CORE_ERROR' => false,
|
||||
'E_CORE_WARNING' => false,
|
||||
'E_COMPILE_ERROR' => false,
|
||||
'E_COMPILE_WARNING' => false,
|
||||
'E_USER_ERROR' => false,
|
||||
'E_USER_WARNING' => false,
|
||||
'E_USER_NOTICE' => false,
|
||||
'E_STRICT' => false,
|
||||
'E_RECOVERABLE_ERROR' => false,
|
||||
'E_DEPRECATED' => false,
|
||||
'E_USER_DEPRECATED' => false,
|
||||
'E_ALL' => false,
|
||||
);
|
||||
|
||||
foreach ( $levels as $level => $reported ) {
|
||||
if ( defined( $level ) ) {
|
||||
$c = constant( $level );
|
||||
if ( $error_reporting & $c ) {
|
||||
$levels[ $level ] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $levels;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function process() {
|
||||
|
||||
global $wp_version;
|
||||
|
||||
$mysql_vars = array(
|
||||
'key_buffer_size' => true, # Key cache size limit
|
||||
'max_allowed_packet' => false, # Individual query size limit
|
||||
'max_connections' => false, # Max number of client connections
|
||||
'query_cache_limit' => true, # Individual query cache size limit
|
||||
'query_cache_size' => true, # Total cache size limit
|
||||
'query_cache_type' => 'ON', # Query cache on or off
|
||||
'innodb_buffer_pool_size' => false, # The amount of memory allocated to the InnoDB buffer pool
|
||||
);
|
||||
|
||||
/** @var QM_Collector_DB_Queries|null */
|
||||
$dbq = QM_Collectors::get( 'db_queries' );
|
||||
|
||||
if ( $dbq ) {
|
||||
if ( method_exists( $dbq->wpdb, 'db_version' ) ) {
|
||||
$server = $dbq->wpdb->db_version();
|
||||
// query_cache_* deprecated since MySQL 5.7.20
|
||||
if ( version_compare( $server, '5.7.20', '>=' ) ) {
|
||||
unset( $mysql_vars['query_cache_limit'], $mysql_vars['query_cache_size'], $mysql_vars['query_cache_type'] );
|
||||
}
|
||||
}
|
||||
|
||||
// phpcs:disable
|
||||
/** @var array<int, stdClass>|null */
|
||||
$variables = $dbq->wpdb->get_results( "
|
||||
SHOW VARIABLES
|
||||
WHERE Variable_name IN ( '" . implode( "', '", array_keys( $mysql_vars ) ) . "' )
|
||||
" );
|
||||
// phpcs:enable
|
||||
|
||||
/** @var mysqli|false|null $dbh */
|
||||
$dbh = $dbq->wpdb->dbh;
|
||||
|
||||
if ( is_object( $dbh ) ) {
|
||||
# mysqli or PDO
|
||||
$extension = get_class( $dbh );
|
||||
} else {
|
||||
# Who knows?
|
||||
$extension = null;
|
||||
}
|
||||
|
||||
$client = mysqli_get_client_version();
|
||||
|
||||
if ( $client ) {
|
||||
$client_version = implode( '.', QM_Util::get_client_version( $client ) );
|
||||
$client_version = sprintf( '%s (%s)', $client, $client_version );
|
||||
} else {
|
||||
$client_version = null;
|
||||
}
|
||||
|
||||
$server_version = self::get_server_version( $dbq->wpdb );
|
||||
|
||||
$info = array(
|
||||
'server-version' => $server_version,
|
||||
'extension' => $extension,
|
||||
'client-version' => $client_version,
|
||||
'user' => $dbq->wpdb->dbuser,
|
||||
'host' => $dbq->wpdb->dbhost,
|
||||
'database' => $dbq->wpdb->dbname,
|
||||
);
|
||||
|
||||
$this->data->db = array(
|
||||
'info' => $info,
|
||||
'vars' => $mysql_vars,
|
||||
'variables' => $variables ?: array(),
|
||||
);
|
||||
}
|
||||
|
||||
$php_data = array(
|
||||
'variables' => array(),
|
||||
);
|
||||
|
||||
$php_data['version'] = phpversion();
|
||||
$php_data['sapi'] = php_sapi_name();
|
||||
$php_data['user'] = self::get_current_user();
|
||||
|
||||
// https://www.php.net/supported-versions.php
|
||||
$php_data['old'] = version_compare( $php_data['version'], '7.4', '<' );
|
||||
|
||||
foreach ( $this->php_vars as $setting ) {
|
||||
$php_data['variables'][ $setting ] = ini_get( $setting ) ?: null;
|
||||
}
|
||||
|
||||
if ( function_exists( 'get_loaded_extensions' ) ) {
|
||||
$extensions = get_loaded_extensions();
|
||||
sort( $extensions, SORT_STRING | SORT_FLAG_CASE );
|
||||
$php_data['extensions'] = array_combine( $extensions, array_map( array( $this, 'get_extension_version' ), $extensions ) ) ?: array();
|
||||
} else {
|
||||
$php_data['extensions'] = array();
|
||||
}
|
||||
|
||||
$php_data['error_reporting'] = error_reporting();
|
||||
$php_data['error_levels'] = self::get_error_levels( $php_data['error_reporting'] );
|
||||
|
||||
$this->data->wp['version'] = $wp_version;
|
||||
$constants = array(
|
||||
'WP_DEBUG' => self::format_bool_constant( 'WP_DEBUG' ),
|
||||
'WP_DEBUG_DISPLAY' => self::format_bool_constant( 'WP_DEBUG_DISPLAY' ),
|
||||
'WP_DEBUG_LOG' => self::format_bool_constant( 'WP_DEBUG_LOG' ),
|
||||
'SCRIPT_DEBUG' => self::format_bool_constant( 'SCRIPT_DEBUG' ),
|
||||
'WP_CACHE' => self::format_bool_constant( 'WP_CACHE' ),
|
||||
'CONCATENATE_SCRIPTS' => self::format_bool_constant( 'CONCATENATE_SCRIPTS' ),
|
||||
'COMPRESS_SCRIPTS' => self::format_bool_constant( 'COMPRESS_SCRIPTS' ),
|
||||
'COMPRESS_CSS' => self::format_bool_constant( 'COMPRESS_CSS' ),
|
||||
'WP_ENVIRONMENT_TYPE' => self::format_bool_constant( 'WP_ENVIRONMENT_TYPE' ),
|
||||
'WP_DEVELOPMENT_MODE' => self::format_bool_constant( 'WP_DEVELOPMENT_MODE' ),
|
||||
);
|
||||
|
||||
if ( function_exists( 'wp_get_environment_type' ) ) {
|
||||
$this->data->wp['environment_type'] = wp_get_environment_type();
|
||||
}
|
||||
|
||||
if ( function_exists( 'wp_get_development_mode' ) ) {
|
||||
$this->data->wp['development_mode'] = wp_get_development_mode();
|
||||
}
|
||||
|
||||
$this->data->wp['constants'] = apply_filters( 'qm/environment-constants', $constants );
|
||||
|
||||
if ( is_multisite() ) {
|
||||
$this->data->wp['constants']['SUNRISE'] = self::format_bool_constant( 'SUNRISE' );
|
||||
}
|
||||
|
||||
if ( isset( $_SERVER['SERVER_SOFTWARE'] ) ) {
|
||||
$server = explode( ' ', wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) );
|
||||
$server = explode( '/', reset( $server ) );
|
||||
} else {
|
||||
$server = array( '' );
|
||||
}
|
||||
|
||||
$server_version = $server[1] ?? null;
|
||||
|
||||
if ( isset( $_SERVER['SERVER_ADDR'] ) ) {
|
||||
$address = wp_unslash( $_SERVER['SERVER_ADDR'] );
|
||||
} else {
|
||||
$address = null;
|
||||
}
|
||||
|
||||
$this->data->php = $php_data;
|
||||
|
||||
$this->data->server = array(
|
||||
'name' => $server[0],
|
||||
'version' => $server_version,
|
||||
'address' => $address,
|
||||
'host' => null,
|
||||
'OS' => null,
|
||||
'arch' => null,
|
||||
);
|
||||
|
||||
if ( function_exists( 'php_uname' ) ) {
|
||||
$this->data->server['host'] = php_uname( 'n' );
|
||||
$this->data->server['OS'] = php_uname( 's' ) . ' ' . php_uname( 'r' );
|
||||
$this->data->server['arch'] = php_uname( 'm' );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $extension
|
||||
* @return string
|
||||
*/
|
||||
public function get_extension_version( $extension ) {
|
||||
// Nothing is simple in PHP. The exif and mysqlnd extensions (and probably others) add a bunch of
|
||||
// crap to their version number, so we need to pluck out the first numeric value in the string.
|
||||
$version = trim( phpversion( $extension ) ?: '' );
|
||||
|
||||
if ( ! $version ) {
|
||||
return $version;
|
||||
}
|
||||
|
||||
$parts = explode( ' ', $version );
|
||||
|
||||
foreach ( $parts as $part ) {
|
||||
if ( $part && is_numeric( $part[0] ) ) {
|
||||
$version = $part;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $version;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param wpdb $db
|
||||
* @return string
|
||||
*/
|
||||
protected static function get_server_version( wpdb $db ) {
|
||||
$version = null;
|
||||
|
||||
if ( method_exists( $db, 'db_server_info' ) ) {
|
||||
$version = $db->db_server_info();
|
||||
}
|
||||
|
||||
if ( ! $version ) {
|
||||
$version = $db->get_var( 'SELECT VERSION()' );
|
||||
}
|
||||
|
||||
if ( ! $version ) {
|
||||
$version = __( 'Unknown', 'query-monitor' );
|
||||
}
|
||||
|
||||
return $version;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected static function get_current_user() {
|
||||
|
||||
$php_u = null;
|
||||
|
||||
if ( function_exists( 'posix_getpwuid' ) && function_exists( 'posix_getuid' ) && function_exists( 'posix_getgrgid' ) ) {
|
||||
$u = posix_getpwuid( posix_getuid() );
|
||||
|
||||
if ( isset( $u['gid'], $u['name'] ) ) {
|
||||
$g = posix_getgrgid( $u['gid'] );
|
||||
|
||||
if ( isset( $g['name'] ) ) {
|
||||
$php_u = $u['name'] . ':' . $g['name'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $php_u ) && isset( $_ENV['APACHE_RUN_USER'] ) ) {
|
||||
$php_u = $_ENV['APACHE_RUN_USER'];
|
||||
if ( isset( $_ENV['APACHE_RUN_GROUP'] ) ) {
|
||||
$php_u .= ':' . $_ENV['APACHE_RUN_GROUP'];
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $php_u ) && isset( $_SERVER['USER'] ) ) {
|
||||
$php_u = wp_unslash( $_SERVER['USER'] );
|
||||
}
|
||||
|
||||
if ( empty( $php_u ) && function_exists( 'exec' ) ) {
|
||||
$php_u = exec( 'whoami' ); // phpcs:ignore
|
||||
}
|
||||
|
||||
if ( empty( $php_u ) && function_exists( 'getenv' ) ) {
|
||||
$php_u = getenv( 'USERNAME' );
|
||||
}
|
||||
|
||||
return $php_u;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, QM_Collector> $collectors
|
||||
* @param QueryMonitor $qm
|
||||
* @return array<string, QM_Collector>
|
||||
*/
|
||||
function register_qm_collector_environment( array $collectors, QueryMonitor $qm ) {
|
||||
$collectors['environment'] = new QM_Collector_Environment();
|
||||
return $collectors;
|
||||
}
|
||||
|
||||
add_filter( 'qm/collectors', 'register_qm_collector_environment', 20, 2 );
|
88
wp-content/plugins/query-monitor/collectors/hooks.php
Normal file
88
wp-content/plugins/query-monitor/collectors/hooks.php
Normal file
@ -0,0 +1,88 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Hooks and actions collector.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @extends QM_DataCollector<QM_Data_Hooks>
|
||||
*/
|
||||
class QM_Collector_Hooks extends QM_DataCollector {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $id = 'hooks';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected static $hide_core;
|
||||
|
||||
public function get_storage(): QM_Data {
|
||||
return new QM_Data_Hooks();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function process() {
|
||||
/**
|
||||
* @var array<string, int> $wp_actions
|
||||
* @var array<string, WP_Hook> $wp_filter
|
||||
*/
|
||||
global $wp_actions, $wp_filter;
|
||||
|
||||
self::$hide_qm = self::hide_qm();
|
||||
self::$hide_core = ( defined( 'QM_HIDE_CORE_ACTIONS' ) && QM_HIDE_CORE_ACTIONS );
|
||||
|
||||
$hooks = array();
|
||||
$all_parts = array();
|
||||
$components = array();
|
||||
|
||||
if ( has_action( 'all' ) ) {
|
||||
$hooks[] = QM_Hook::process( 'all', 'action', $wp_filter, self::$hide_qm, self::$hide_core );
|
||||
}
|
||||
|
||||
$this->data->all_hooks = defined( 'QM_SHOW_ALL_HOOKS' ) && QM_SHOW_ALL_HOOKS;
|
||||
|
||||
if ( $this->data->all_hooks ) {
|
||||
// Show all hooks
|
||||
$hook_names = array_keys( $wp_filter );
|
||||
} else {
|
||||
// Only show action hooks that have been called at least once
|
||||
$hook_names = array_keys( $wp_actions );
|
||||
}
|
||||
|
||||
foreach ( $hook_names as $name ) {
|
||||
$type = 'action';
|
||||
|
||||
if ( $this->data->all_hooks ) {
|
||||
$type = array_key_exists( $name, $wp_actions ) ? 'action' : 'filter';
|
||||
}
|
||||
|
||||
$hook = QM_Hook::process( $name, $type, $wp_filter, self::$hide_qm, self::$hide_core );
|
||||
$hooks[] = $hook;
|
||||
|
||||
$all_parts = array_merge( $all_parts, $hook['parts'] );
|
||||
$components = array_merge( $components, $hook['components'] );
|
||||
|
||||
}
|
||||
|
||||
$this->data->hooks = $hooks;
|
||||
$this->data->parts = array_unique( array_filter( $all_parts ) );
|
||||
$this->data->components = array_unique( array_filter( $components ) );
|
||||
|
||||
usort( $this->data->parts, 'strcasecmp' );
|
||||
usort( $this->data->components, 'strcasecmp' );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Load early to catch all hooks
|
||||
QM_Collectors::add( new QM_Collector_Hooks() );
|
399
wp-content/plugins/query-monitor/collectors/http.php
Normal file
399
wp-content/plugins/query-monitor/collectors/http.php
Normal file
@ -0,0 +1,399 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* HTTP API request collector.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @extends QM_DataCollector<QM_Data_HTTP>
|
||||
*/
|
||||
class QM_Collector_HTTP extends QM_DataCollector {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $id = 'http';
|
||||
|
||||
/**
|
||||
* @var mixed|null
|
||||
*/
|
||||
private $info = null;
|
||||
|
||||
/**
|
||||
* @var array<string, array<string, mixed>>
|
||||
* @phpstan-var array<string, array{
|
||||
* url: string,
|
||||
* start: float,
|
||||
* args: array<string, mixed>,
|
||||
* filtered_trace: list<array<string, mixed>>,
|
||||
* component: QM_Component,
|
||||
* }>
|
||||
*/
|
||||
private $http_requests = array();
|
||||
|
||||
/**
|
||||
* @var array<string, array<string, mixed>>
|
||||
* @phpstan-var array<string, array{
|
||||
* end: float,
|
||||
* args: array<string, mixed>,
|
||||
* response: mixed[]|WP_Error,
|
||||
* info: array<string, mixed>|null,
|
||||
* }>
|
||||
*/
|
||||
private $http_responses = array();
|
||||
|
||||
public function get_storage(): QM_Data {
|
||||
return new QM_Data_HTTP();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function set_up() {
|
||||
|
||||
parent::set_up();
|
||||
|
||||
add_filter( 'http_request_args', array( $this, 'filter_http_request_args' ), 9999, 2 );
|
||||
add_filter( 'pre_http_request', array( $this, 'filter_pre_http_request' ), 9999, 3 );
|
||||
add_action( 'http_api_debug', array( $this, 'action_http_api_debug' ), 9999, 5 );
|
||||
|
||||
add_action( 'requests-curl.after_request', array( $this, 'action_curl_after_request' ), 9999, 2 );
|
||||
add_action( 'requests-fsockopen.after_request', array( $this, 'action_fsockopen_after_request' ), 9999, 2 );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function tear_down() {
|
||||
remove_filter( 'http_request_args', array( $this, 'filter_http_request_args' ), 9999 );
|
||||
remove_filter( 'pre_http_request', array( $this, 'filter_pre_http_request' ), 9999 );
|
||||
remove_action( 'http_api_debug', array( $this, 'action_http_api_debug' ), 9999 );
|
||||
|
||||
remove_action( 'requests-curl.before_request', array( $this, 'action_curl_before_request' ), 9999 );
|
||||
remove_action( 'requests-curl.after_request', array( $this, 'action_curl_after_request' ), 9999 );
|
||||
remove_action( 'requests-fsockopen.before_request', array( $this, 'action_fsockopen_before_request' ), 9999 );
|
||||
remove_action( 'requests-fsockopen.after_request', array( $this, 'action_fsockopen_after_request' ), 9999 );
|
||||
|
||||
parent::tear_down();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_concerned_actions() {
|
||||
$actions = array(
|
||||
'http_api_curl',
|
||||
'requests-multiple.request.complete',
|
||||
'requests-request.progress',
|
||||
'requests-transport.internal.parse_error',
|
||||
'requests-transport.internal.parse_response',
|
||||
);
|
||||
$transports = array(
|
||||
'requests',
|
||||
'curl',
|
||||
'fsockopen',
|
||||
);
|
||||
|
||||
foreach ( $transports as $transport ) {
|
||||
$actions[] = "requests-{$transport}.after_headers";
|
||||
$actions[] = "requests-{$transport}.after_multi_exec";
|
||||
$actions[] = "requests-{$transport}.after_request";
|
||||
$actions[] = "requests-{$transport}.after_send";
|
||||
$actions[] = "requests-{$transport}.before_multi_add";
|
||||
$actions[] = "requests-{$transport}.before_multi_exec";
|
||||
$actions[] = "requests-{$transport}.before_parse";
|
||||
$actions[] = "requests-{$transport}.before_redirect";
|
||||
$actions[] = "requests-{$transport}.before_redirect_check";
|
||||
$actions[] = "requests-{$transport}.before_request";
|
||||
$actions[] = "requests-{$transport}.before_send";
|
||||
$actions[] = "requests-{$transport}.remote_host_path";
|
||||
$actions[] = "requests-{$transport}.remote_socket";
|
||||
}
|
||||
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_concerned_filters() {
|
||||
return array(
|
||||
'block_local_requests',
|
||||
'http_request_args',
|
||||
'http_response',
|
||||
'https_local_ssl_verify',
|
||||
'https_ssl_verify',
|
||||
'pre_http_request',
|
||||
'use_curl_transport',
|
||||
'use_streams_transport',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_concerned_constants() {
|
||||
return array(
|
||||
'WP_PROXY_HOST',
|
||||
'WP_PROXY_PORT',
|
||||
'WP_PROXY_USERNAME',
|
||||
'WP_PROXY_PASSWORD',
|
||||
'WP_PROXY_BYPASS_HOSTS',
|
||||
'WP_HTTP_BLOCK_EXTERNAL',
|
||||
'WP_ACCESSIBLE_HOSTS',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the arguments used in an HTTP request.
|
||||
*
|
||||
* Used to log the request, and to add the logging key to the arguments array.
|
||||
*
|
||||
* @param array<string, mixed> $args HTTP request arguments.
|
||||
* @param string $url The request URL.
|
||||
* @return array<string, mixed> HTTP request arguments.
|
||||
*/
|
||||
public function filter_http_request_args( array $args, $url ) {
|
||||
$trace = new QM_Backtrace( array(
|
||||
'ignore_hook' => array(
|
||||
current_filter() => true,
|
||||
),
|
||||
'ignore_class' => array(
|
||||
'WP_Http' => true,
|
||||
),
|
||||
'ignore_func' => array(
|
||||
'wp_safe_remote_request' => true,
|
||||
'wp_safe_remote_get' => true,
|
||||
'wp_safe_remote_post' => true,
|
||||
'wp_safe_remote_head' => true,
|
||||
'wp_remote_request' => true,
|
||||
'wp_remote_get' => true,
|
||||
'wp_remote_post' => true,
|
||||
'wp_remote_head' => true,
|
||||
'wp_remote_fopen' => true,
|
||||
'download_url' => true,
|
||||
'vip_safe_wp_remote_get' => true,
|
||||
'vip_safe_wp_remote_request' => true,
|
||||
'wpcom_vip_file_get_contents' => true,
|
||||
),
|
||||
) );
|
||||
|
||||
if ( isset( $args['_qm_key'], $this->http_requests[ $args['_qm_key'] ] ) ) {
|
||||
// Something has triggered another HTTP request from within the `pre_http_request` filter
|
||||
// (eg. WordPress Beta Tester does this). This allows for one level of nested queries.
|
||||
$args['_qm_original_key'] = $args['_qm_key'];
|
||||
$start = $this->http_requests[ $args['_qm_key'] ]['start'];
|
||||
} else {
|
||||
$start = microtime( true );
|
||||
}
|
||||
|
||||
$key = microtime( true ) . $url;
|
||||
$this->http_requests[ $key ] = array(
|
||||
'url' => $url,
|
||||
'args' => $args,
|
||||
'start' => $start,
|
||||
'filtered_trace' => $trace->get_filtered_trace(),
|
||||
'component' => $trace->get_component(),
|
||||
);
|
||||
$args['_qm_key'] = $key;
|
||||
return $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the HTTP request's response if it's being short-circuited by another plugin.
|
||||
* This is necessary due to https://core.trac.wordpress.org/ticket/25747
|
||||
*
|
||||
* $response should be one of boolean false, an array, or a `WP_Error`, but be aware that plugins
|
||||
* which short-circuit the request using this filter may (incorrectly) return data of another type.
|
||||
*
|
||||
* @param false|mixed[]|WP_Error $response The preemptive HTTP response. Default false.
|
||||
* @param array<string, mixed> $args HTTP request arguments.
|
||||
* @param string $url The request URL.
|
||||
* @return false|mixed[]|WP_Error The preemptive HTTP response.
|
||||
*/
|
||||
public function filter_pre_http_request( $response, array $args, $url ) {
|
||||
|
||||
// All is well:
|
||||
if ( false === $response ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
// Something's filtering the response, so we'll log it
|
||||
$this->log_http_response( $response, $args, $url );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Debugging action for the HTTP API.
|
||||
*
|
||||
* @param mixed $response A parameter which varies depending on $action.
|
||||
* @param string $action The debug action. Currently one of 'response' or 'transports_list'.
|
||||
* @param string $class The HTTP transport class name.
|
||||
* @param array<string, mixed> $args HTTP request arguments.
|
||||
* @param string $url The request URL.
|
||||
* @return void
|
||||
*/
|
||||
public function action_http_api_debug( $response, $action, $class, $args, $url ) {
|
||||
switch ( $action ) {
|
||||
|
||||
case 'response':
|
||||
$this->log_http_response( $response, $args, $url );
|
||||
|
||||
break;
|
||||
|
||||
case 'transports_list':
|
||||
# Nothing
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $headers
|
||||
* @param mixed[] $info
|
||||
* @return void
|
||||
*/
|
||||
public function action_curl_after_request( $headers, array $info = null ) {
|
||||
$this->info = $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $headers
|
||||
* @param mixed[] $info
|
||||
* @return void
|
||||
*/
|
||||
public function action_fsockopen_after_request( $headers, array $info = null ) {
|
||||
$this->info = $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log an HTTP response.
|
||||
*
|
||||
* @param mixed[]|WP_Error $response The HTTP response.
|
||||
* @param array<string, mixed> $args HTTP request arguments.
|
||||
* @param string $url The request URL.
|
||||
* @return void
|
||||
*/
|
||||
public function log_http_response( $response, array $args, $url ) {
|
||||
/** @var string */
|
||||
$key = $args['_qm_key'];
|
||||
|
||||
$http_response = array(
|
||||
'end' => microtime( true ),
|
||||
'response' => $response,
|
||||
'args' => $args,
|
||||
'info' => $this->info,
|
||||
);
|
||||
|
||||
if ( isset( $args['_qm_original_key'] ) ) {
|
||||
/** @var string */
|
||||
$original_key = $args['_qm_original_key'];
|
||||
$this->http_responses[ $original_key ]['end'] = $this->http_requests[ $original_key ]['start'];
|
||||
$this->http_responses[ $original_key ]['response'] = new WP_Error( 'http_request_not_executed', sprintf(
|
||||
/* translators: %s: Hook name */
|
||||
__( 'Request not executed due to a filter on %s', 'query-monitor' ),
|
||||
'pre_http_request'
|
||||
) );
|
||||
}
|
||||
|
||||
$this->http_responses[ $key ] = $http_response;
|
||||
|
||||
$this->info = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function process() {
|
||||
$this->data->ltime = 0;
|
||||
|
||||
if ( empty( $this->http_requests ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* List of HTTP API error codes to ignore.
|
||||
*
|
||||
* @since 2.7.0
|
||||
*
|
||||
* @param array $http_errors Array of HTTP errors.
|
||||
*/
|
||||
$silent = apply_filters( 'qm/collect/silent_http_errors', array(
|
||||
'http_request_not_executed',
|
||||
'airplane_mode_enabled',
|
||||
) );
|
||||
|
||||
$home_host = (string) parse_url( home_url(), PHP_URL_HOST );
|
||||
|
||||
foreach ( $this->http_requests as $key => $request ) {
|
||||
$response = $this->http_responses[ $key ];
|
||||
|
||||
if ( empty( $response['response'] ) ) {
|
||||
// Timed out
|
||||
$response['response'] = new WP_Error( 'http_request_timed_out', __( 'Request timed out', 'query-monitor' ) );
|
||||
$response['end'] = floatval( $request['start'] + $response['args']['timeout'] );
|
||||
}
|
||||
|
||||
if ( $response['response'] instanceof WP_Error ) {
|
||||
if ( ! in_array( $response['response']->get_error_code(), $silent, true ) ) {
|
||||
$this->data->errors['alert'][] = $key;
|
||||
}
|
||||
$type = 'error';
|
||||
} elseif ( ! $response['args']['blocking'] ) {
|
||||
$type = 'non-blocking';
|
||||
} else {
|
||||
$code = intval( wp_remote_retrieve_response_code( $response['response'] ) );
|
||||
$type = "http:{$code}";
|
||||
if ( ( $code >= 400 ) && ( 'HEAD' !== $request['args']['method'] ) ) {
|
||||
$this->data->errors['warning'][] = $key;
|
||||
}
|
||||
}
|
||||
|
||||
$ltime = ( $response['end'] - $request['start'] );
|
||||
$redirected_to = null;
|
||||
|
||||
if ( isset( $response['info'] ) && ! empty( $response['info']['url'] ) && is_string( $response['info']['url'] ) ) {
|
||||
// Ignore query variables when detecting a redirect.
|
||||
$from = untrailingslashit( preg_replace( '#\?[^$]+$#', '', $request['url'] ) );
|
||||
$to = untrailingslashit( preg_replace( '#\?[^$]+$#', '', $response['info']['url'] ) );
|
||||
if ( $from !== $to ) {
|
||||
$redirected_to = $response['info']['url'];
|
||||
}
|
||||
}
|
||||
|
||||
$this->data->ltime += $ltime;
|
||||
|
||||
$host = (string) parse_url( $request['url'], PHP_URL_HOST );
|
||||
$local = ( $host === $home_host );
|
||||
|
||||
$this->log_type( $type );
|
||||
$this->log_component( $request['component'], $ltime, $type );
|
||||
$this->data->http[ $key ] = array(
|
||||
'args' => $response['args'],
|
||||
'component' => $request['component'],
|
||||
'filtered_trace' => $request['filtered_trace'],
|
||||
'host' => $host,
|
||||
'info' => $response['info'],
|
||||
'local' => $local,
|
||||
'ltime' => $ltime,
|
||||
'redirected_to' => $redirected_to,
|
||||
'response' => $response['response'],
|
||||
'type' => $type,
|
||||
'url' => $request['url'],
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Load early in case a plugin is doing an HTTP request when it initialises instead of after the `plugins_loaded` hook
|
||||
QM_Collectors::add( new QM_Collector_HTTP() );
|
222
wp-content/plugins/query-monitor/collectors/languages.php
Normal file
222
wp-content/plugins/query-monitor/collectors/languages.php
Normal file
@ -0,0 +1,222 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Language and locale collector.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @extends QM_DataCollector<QM_Data_Languages>
|
||||
*/
|
||||
class QM_Collector_Languages extends QM_DataCollector {
|
||||
|
||||
public $id = 'languages';
|
||||
|
||||
public function get_storage(): QM_Data {
|
||||
return new QM_Data_Languages();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function set_up() {
|
||||
|
||||
parent::set_up();
|
||||
|
||||
add_filter( 'load_textdomain_mofile', array( $this, 'log_file_load' ), 9999, 2 );
|
||||
add_filter( 'load_script_translation_file', array( $this, 'log_script_file_load' ), 9999, 3 );
|
||||
add_action( 'init', array( $this, 'collect_locale_data' ), 9999 );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function tear_down() {
|
||||
remove_filter( 'load_textdomain_mofile', array( $this, 'log_file_load' ), 9999 );
|
||||
remove_filter( 'load_script_translation_file', array( $this, 'log_script_file_load' ), 9999 );
|
||||
remove_action( 'init', array( $this, 'collect_locale_data' ), 9999 );
|
||||
|
||||
parent::tear_down();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function collect_locale_data() {
|
||||
$this->data->locale = get_locale();
|
||||
$this->data->user_locale = function_exists( 'get_user_locale' ) ? get_user_locale() : get_locale();
|
||||
$this->data->determined_locale = function_exists( 'determine_locale' ) ? determine_locale() : get_locale();
|
||||
$this->data->language_attributes = get_language_attributes();
|
||||
|
||||
if ( function_exists( '\Inpsyde\MultilingualPress\siteLanguageTag' ) ) {
|
||||
$this->data->mlp_language = \Inpsyde\MultilingualPress\siteLanguageTag();
|
||||
}
|
||||
|
||||
if ( function_exists( 'pll_current_language' ) ) {
|
||||
$this->data->pll_language = pll_current_language();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_concerned_actions() {
|
||||
return array(
|
||||
'load_textdomain',
|
||||
'unload_textdomain',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_concerned_filters() {
|
||||
return array(
|
||||
'determine_locale',
|
||||
'gettext',
|
||||
'gettext_with_context',
|
||||
'language_attributes',
|
||||
'load_script_textdomain_relative_path',
|
||||
'load_script_translation_file',
|
||||
'load_script_translations',
|
||||
'load_textdomain_mofile',
|
||||
'locale',
|
||||
'ngettext',
|
||||
'ngettext_with_context',
|
||||
'override_load_textdomain',
|
||||
'override_unload_textdomain',
|
||||
'plugin_locale',
|
||||
'pre_determine_locale',
|
||||
'pre_load_script_translations',
|
||||
'theme_locale',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_concerned_options() {
|
||||
return array(
|
||||
'WPLANG',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_concerned_constants() {
|
||||
return array(
|
||||
'WPLANG',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function process() {
|
||||
if ( empty( $this->data->languages ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->data->total_size = 0;
|
||||
|
||||
ksort( $this->data->languages );
|
||||
|
||||
foreach ( $this->data->languages as & $mofiles ) {
|
||||
foreach ( $mofiles as & $mofile ) {
|
||||
if ( $mofile['found'] ) {
|
||||
$this->data->total_size += $mofile['found'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store log data.
|
||||
*
|
||||
* @param mixed $mofile Should be a string path to the MO file, could be anything.
|
||||
* @param string $domain Text domain.
|
||||
* @return string
|
||||
*/
|
||||
public function log_file_load( $mofile, $domain ) {
|
||||
if ( 'query-monitor' === $domain && self::hide_qm() ) {
|
||||
return $mofile;
|
||||
}
|
||||
|
||||
if ( is_string( $mofile ) && isset( $this->data->languages[ $domain ][ $mofile ] ) ) {
|
||||
return $mofile;
|
||||
}
|
||||
|
||||
$trace = new QM_Backtrace( array(
|
||||
'ignore_hook' => array(
|
||||
current_filter() => true,
|
||||
),
|
||||
'ignore_func' => array(
|
||||
'load_textdomain' => ( 'default' !== $domain ),
|
||||
'load_muplugin_textdomain' => true,
|
||||
'load_plugin_textdomain' => true,
|
||||
'load_theme_textdomain' => true,
|
||||
'load_child_theme_textdomain' => true,
|
||||
'load_default_textdomain' => true,
|
||||
),
|
||||
) );
|
||||
|
||||
$found = ( is_string( $mofile ) ) && file_exists( $mofile ) ? filesize( $mofile ) : false;
|
||||
|
||||
if ( ! is_string( $mofile ) ) {
|
||||
$mofile = gettype( $mofile );
|
||||
}
|
||||
|
||||
$this->data->languages[ $domain ][ $mofile ] = array(
|
||||
'caller' => $trace->get_caller(),
|
||||
'domain' => $domain,
|
||||
'file' => $mofile,
|
||||
'found' => $found,
|
||||
'handle' => null,
|
||||
'type' => 'gettext',
|
||||
);
|
||||
|
||||
return $mofile;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the file path for loading script translations for the given script handle and textdomain.
|
||||
*
|
||||
* @param string|false $file Path to the translation file to load. False if there isn't one.
|
||||
* @param string $handle Name of the script to register a translation domain to.
|
||||
* @param string $domain The textdomain.
|
||||
*
|
||||
* @return string|false Path to the translation file to load. False if there isn't one.
|
||||
*/
|
||||
public function log_script_file_load( $file, $handle, $domain ) {
|
||||
$trace = new QM_Backtrace( array(
|
||||
'ignore_hook' => array(
|
||||
current_filter() => true,
|
||||
),
|
||||
) );
|
||||
|
||||
$found = ( $file && file_exists( $file ) ) ? filesize( $file ) : false;
|
||||
$key = $file ?: uniqid();
|
||||
|
||||
$this->data->languages[ $domain ][ $key ] = array(
|
||||
'caller' => $trace->get_caller(),
|
||||
'domain' => $domain,
|
||||
'file' => $file,
|
||||
'found' => $found,
|
||||
'handle' => $handle,
|
||||
'type' => 'jed',
|
||||
);
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Load early to catch early errors
|
||||
QM_Collectors::add( new QM_Collector_Languages() );
|
285
wp-content/plugins/query-monitor/collectors/logger.php
Normal file
285
wp-content/plugins/query-monitor/collectors/logger.php
Normal file
@ -0,0 +1,285 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* PSR-3 compatible logging collector.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @extends QM_DataCollector<QM_Data_Logger>
|
||||
* @phpstan-type LogMessage WP_Error|Throwable|string|bool|null
|
||||
*/
|
||||
class QM_Collector_Logger extends QM_DataCollector {
|
||||
|
||||
public $id = 'logger';
|
||||
|
||||
public const EMERGENCY = 'emergency';
|
||||
public const ALERT = 'alert';
|
||||
public const CRITICAL = 'critical';
|
||||
public const ERROR = 'error';
|
||||
public const WARNING = 'warning';
|
||||
public const NOTICE = 'notice';
|
||||
public const INFO = 'info';
|
||||
public const DEBUG = 'debug';
|
||||
|
||||
public function get_storage(): QM_Data {
|
||||
return new QM_Data_Logger();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function set_up() {
|
||||
parent::set_up();
|
||||
|
||||
$this->data->counts = array_fill_keys( $this->get_levels(), 0 );
|
||||
|
||||
foreach ( $this->get_levels() as $level ) {
|
||||
add_action( "qm/{$level}", array( $this, $level ), 10, 2 );
|
||||
}
|
||||
|
||||
add_action( 'qm/log', array( $this, 'log' ), 10, 3 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function tear_down() {
|
||||
foreach ( $this->get_levels() as $level ) {
|
||||
remove_action( "qm/{$level}", array( $this, $level ), 10 );
|
||||
}
|
||||
|
||||
remove_action( 'qm/log', array( $this, 'log' ), 10 );
|
||||
|
||||
parent::tear_down();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $message
|
||||
* @param array<string, mixed> $context
|
||||
* @phpstan-param LogMessage $message
|
||||
* @return void
|
||||
*/
|
||||
public function emergency( $message, array $context = array() ) {
|
||||
$this->store( self::EMERGENCY, $message, $context );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $message
|
||||
* @param array<string, mixed> $context
|
||||
* @phpstan-param LogMessage $message
|
||||
* @return void
|
||||
*/
|
||||
public function alert( $message, array $context = array() ) {
|
||||
$this->store( self::ALERT, $message, $context );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $message
|
||||
* @param array<string, mixed> $context
|
||||
* @phpstan-param LogMessage $message
|
||||
* @return void
|
||||
*/
|
||||
public function critical( $message, array $context = array() ) {
|
||||
$this->store( self::CRITICAL, $message, $context );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $message
|
||||
* @param array<string, mixed> $context
|
||||
* @phpstan-param LogMessage $message
|
||||
* @return void
|
||||
*/
|
||||
public function error( $message, array $context = array() ) {
|
||||
$this->store( self::ERROR, $message, $context );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $message
|
||||
* @param array<string, mixed> $context
|
||||
* @phpstan-param LogMessage $message
|
||||
* @return void
|
||||
*/
|
||||
public function warning( $message, array $context = array() ) {
|
||||
$this->store( self::WARNING, $message, $context );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $message
|
||||
* @param array<string, mixed> $context
|
||||
* @phpstan-param LogMessage $message
|
||||
* @return void
|
||||
*/
|
||||
public function notice( $message, array $context = array() ) {
|
||||
$this->store( self::NOTICE, $message, $context );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $message
|
||||
* @param array<string, mixed> $context
|
||||
* @phpstan-param LogMessage $message
|
||||
* @return void
|
||||
*/
|
||||
public function info( $message, array $context = array() ) {
|
||||
$this->store( self::INFO, $message, $context );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $message
|
||||
* @param array<string, mixed> $context
|
||||
* @phpstan-param LogMessage $message
|
||||
* @return void
|
||||
*/
|
||||
public function debug( $message, array $context = array() ) {
|
||||
$this->store( self::DEBUG, $message, $context );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $level
|
||||
* @param mixed $message
|
||||
* @param array<string, mixed> $context
|
||||
* @phpstan-param self::* $level
|
||||
* @phpstan-param LogMessage $message
|
||||
* @return void
|
||||
*/
|
||||
public function log( $level, $message, array $context = array() ) {
|
||||
if ( ! in_array( $level, $this->get_levels(), true ) ) {
|
||||
throw new InvalidArgumentException( 'Unsupported log level' );
|
||||
}
|
||||
|
||||
$this->store( $level, $message, $context );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $level
|
||||
* @param mixed $message
|
||||
* @param array<string, mixed> $context
|
||||
* @phpstan-param self::* $level
|
||||
* @phpstan-param LogMessage $message
|
||||
* @return void
|
||||
*/
|
||||
protected function store( $level, $message, array $context = array() ) {
|
||||
$trace = new QM_Backtrace( array(
|
||||
'ignore_hook' => array(
|
||||
current_filter() => true,
|
||||
),
|
||||
) );
|
||||
|
||||
if ( $message instanceof WP_Error ) {
|
||||
$message = sprintf(
|
||||
'WP_Error: %s (%s)',
|
||||
$message->get_error_message(),
|
||||
$message->get_error_code()
|
||||
);
|
||||
}
|
||||
|
||||
if ( $message instanceof Throwable ) {
|
||||
$message = sprintf(
|
||||
'%1$s: %2$s',
|
||||
get_class( $message ),
|
||||
$message->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! is_string( $message ) ) {
|
||||
if ( null === $message ) {
|
||||
$message = 'null';
|
||||
} elseif ( false === $message ) {
|
||||
$message = 'false';
|
||||
} elseif ( true === $message ) {
|
||||
$message = 'true';
|
||||
}
|
||||
|
||||
$message = print_r( $message, true );
|
||||
} elseif ( '' === trim( $message ) ) {
|
||||
$message = '(Empty string)';
|
||||
}
|
||||
|
||||
$this->data->counts[ $level ]++;
|
||||
$this->data->logs[] = array(
|
||||
'message' => self::interpolate( $message, $context ),
|
||||
'filtered_trace' => $trace->get_filtered_trace(),
|
||||
'component' => $trace->get_component(),
|
||||
'level' => $level,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param array<string, mixed> $context
|
||||
* @return string
|
||||
*/
|
||||
protected static function interpolate( $message, array $context = array() ) {
|
||||
// build a replacement array with braces around the context keys
|
||||
$replace = array();
|
||||
|
||||
foreach ( $context as $key => $val ) {
|
||||
// check that the value can be casted to string
|
||||
if ( is_bool( $val ) ) {
|
||||
$replace[ "{{$key}}" ] = ( $val ? 'true' : 'false' );
|
||||
} elseif ( is_scalar( $val ) ) {
|
||||
$replace[ "{{$key}}" ] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
// interpolate replacement values into the message and return
|
||||
return strtr( $message, $replace );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function process() {
|
||||
if ( empty( $this->data->logs ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$components = array();
|
||||
|
||||
foreach ( $this->data->logs as $row ) {
|
||||
$component = $row['component'];
|
||||
$components[ $component->name ] = $component->name;
|
||||
}
|
||||
|
||||
$this->data->components = $components;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
* @phpstan-return list<self::*>
|
||||
*/
|
||||
public function get_levels() {
|
||||
return array(
|
||||
self::EMERGENCY,
|
||||
self::ALERT,
|
||||
self::CRITICAL,
|
||||
self::ERROR,
|
||||
self::WARNING,
|
||||
self::NOTICE,
|
||||
self::INFO,
|
||||
self::DEBUG,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
* @phpstan-return list<self::*>
|
||||
*/
|
||||
public function get_warning_levels() {
|
||||
return array(
|
||||
self::EMERGENCY,
|
||||
self::ALERT,
|
||||
self::CRITICAL,
|
||||
self::ERROR,
|
||||
self::WARNING,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Load early in case a plugin wants to log a message early in the bootstrap process
|
||||
QM_Collectors::add( new QM_Collector_Logger() );
|
64
wp-content/plugins/query-monitor/collectors/multisite.php
Normal file
64
wp-content/plugins/query-monitor/collectors/multisite.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Multisite collector, used for monitoring use of `switch_to_blog()` and `restore_current_blog()`.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @extends QM_DataCollector<QM_Data_Multisite>
|
||||
*/
|
||||
class QM_Collector_Multisite extends QM_DataCollector {
|
||||
public $id = 'multisite';
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
|
||||
$this->data->switches = array();
|
||||
|
||||
add_action( 'switch_blog', array( $this, 'action_switch_blog' ), 10, 3 );
|
||||
}
|
||||
|
||||
public function get_storage(): QM_Data {
|
||||
return new QM_Data_Multisite();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires when the blog is switched.
|
||||
*
|
||||
* @param int $new_blog_id New blog ID.
|
||||
* @param int $prev_blog_id Previous blog ID.
|
||||
* @param string $context Additional context. Accepts 'switch' when called from switch_to_blog()
|
||||
* or 'restore' when called from restore_current_blog().
|
||||
* @return void
|
||||
*/
|
||||
public function action_switch_blog( $new_blog_id, $prev_blog_id, $context ) {
|
||||
if ( intval( $new_blog_id ) === intval( $prev_blog_id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->data->switches[] = array(
|
||||
'new' => $new_blog_id,
|
||||
'prev' => $prev_blog_id,
|
||||
'to' => ( 'switch' === $context ),
|
||||
'trace' => new QM_Backtrace( array(
|
||||
'ignore_hook' => array(
|
||||
'switch_blog' => true,
|
||||
),
|
||||
'ignore_func' => array(
|
||||
'switch_to_blog' => true,
|
||||
'restore_current_blog' => true,
|
||||
),
|
||||
) ),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ( is_multisite() ) {
|
||||
# Load early to detect as many happenings during the bootstrap process as possible
|
||||
QM_Collectors::add( new QM_Collector_Multisite() );
|
||||
}
|
114
wp-content/plugins/query-monitor/collectors/overview.php
Normal file
114
wp-content/plugins/query-monitor/collectors/overview.php
Normal file
@ -0,0 +1,114 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* General overview collector.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @extends QM_DataCollector<QM_Data_Overview>
|
||||
*/
|
||||
class QM_Collector_Overview extends QM_DataCollector {
|
||||
|
||||
public $id = 'overview';
|
||||
|
||||
public function get_storage(): QM_Data {
|
||||
return new QM_Data_Overview();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function set_up() {
|
||||
parent::set_up();
|
||||
|
||||
add_action( 'shutdown', array( $this, 'process_timing' ), 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function tear_down() {
|
||||
remove_action( 'shutdown', array( $this, 'process_timing' ), 0 );
|
||||
|
||||
parent::tear_down();
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the timing and memory related stats as early as possible, so the
|
||||
* data isn't skewed by collectors that are processed before this one.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process_timing() {
|
||||
$this->data->time_taken = self::timer_stop_float();
|
||||
|
||||
if ( function_exists( 'memory_get_peak_usage' ) ) {
|
||||
$this->data->memory = memory_get_peak_usage();
|
||||
} elseif ( function_exists( 'memory_get_usage' ) ) {
|
||||
$this->data->memory = memory_get_usage();
|
||||
} else {
|
||||
$this->data->memory = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function process() {
|
||||
if ( ! isset( $this->data->time_taken ) ) {
|
||||
$this->process_timing();
|
||||
}
|
||||
|
||||
$this->data->time_limit = (int) ini_get( 'max_execution_time' );
|
||||
$this->data->time_start = $_SERVER['REQUEST_TIME_FLOAT'];
|
||||
|
||||
if ( ! empty( $this->data->time_limit ) ) {
|
||||
$this->data->time_usage = ( 100 / $this->data->time_limit ) * $this->data->time_taken;
|
||||
} else {
|
||||
$this->data->time_usage = 0;
|
||||
}
|
||||
|
||||
if ( is_user_logged_in() ) {
|
||||
$this->data->current_user = self::format_user( wp_get_current_user() );
|
||||
} else {
|
||||
$this->data->current_user = null;
|
||||
}
|
||||
|
||||
if ( function_exists( 'current_user_switched' ) && current_user_switched() ) {
|
||||
$this->data->switched_user = self::format_user( current_user_switched() );
|
||||
} else {
|
||||
$this->data->switched_user = null;
|
||||
}
|
||||
|
||||
$this->data->memory_limit = QM_Util::convert_hr_to_bytes( ini_get( 'memory_limit' ) ?: '0' );
|
||||
|
||||
if ( $this->data->memory_limit > 0 ) {
|
||||
$this->data->memory_usage = ( 100 / $this->data->memory_limit ) * $this->data->memory;
|
||||
} else {
|
||||
$this->data->memory_usage = 0;
|
||||
}
|
||||
|
||||
$this->data->display_time_usage_warning = ( $this->data->time_usage >= 75 );
|
||||
$this->data->display_memory_usage_warning = ( $this->data->memory_usage >= 75 );
|
||||
|
||||
$this->data->is_admin = is_admin();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, QM_Collector> $collectors
|
||||
* @param QueryMonitor $qm
|
||||
* @return array<string, QM_Collector>
|
||||
*/
|
||||
function register_qm_collector_overview( array $collectors, QueryMonitor $qm ) {
|
||||
$collectors['overview'] = new QM_Collector_Overview();
|
||||
return $collectors;
|
||||
}
|
||||
|
||||
add_filter( 'qm/collectors', 'register_qm_collector_overview', 1, 2 );
|
562
wp-content/plugins/query-monitor/collectors/php_errors.php
Normal file
562
wp-content/plugins/query-monitor/collectors/php_errors.php
Normal file
@ -0,0 +1,562 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* PHP error collector.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( ! defined( 'QM_ERROR_FATALS' ) ) {
|
||||
define( 'QM_ERROR_FATALS', E_ERROR | E_PARSE | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR );
|
||||
}
|
||||
|
||||
/**
|
||||
* @extends QM_DataCollector<QM_Data_PHP_Errors>
|
||||
* @phpstan-type errorLabels array{
|
||||
* warning: string,
|
||||
* notice: string,
|
||||
* strict: string,
|
||||
* deprecated: string,
|
||||
* }
|
||||
* @phpstan-import-type errorObject from QM_Data_PHP_Errors
|
||||
*/
|
||||
class QM_Collector_PHP_Errors extends QM_DataCollector {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $id = 'php_errors';
|
||||
|
||||
/**
|
||||
* @var array<string, array<string, string>>
|
||||
* @phpstan-var array{
|
||||
* errors: errorLabels,
|
||||
* suppressed: errorLabels,
|
||||
* silenced: errorLabels,
|
||||
* }
|
||||
*/
|
||||
public $types;
|
||||
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private $error_reporting = null;
|
||||
|
||||
/**
|
||||
* @var string|false|null
|
||||
*/
|
||||
private $display_errors = null;
|
||||
|
||||
/**
|
||||
* @var callable|null
|
||||
*/
|
||||
private $previous_error_handler = null;
|
||||
|
||||
/**
|
||||
* @var callable|null
|
||||
*/
|
||||
private $previous_exception_handler = null;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private static $unexpected_error = null;
|
||||
|
||||
public function get_storage(): QM_Data {
|
||||
return new QM_Data_PHP_Errors();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function set_up() {
|
||||
if ( defined( 'QM_DISABLE_ERROR_HANDLER' ) && QM_DISABLE_ERROR_HANDLER ) {
|
||||
return;
|
||||
}
|
||||
|
||||
parent::set_up();
|
||||
|
||||
// Capture the last error that occurred before QM loaded:
|
||||
$prior_error = error_get_last();
|
||||
|
||||
// Non-fatal error handler:
|
||||
$this->previous_error_handler = set_error_handler( array( $this, 'error_handler' ), ( E_ALL ^ QM_ERROR_FATALS ) );
|
||||
|
||||
// Fatal error and uncaught exception handler:
|
||||
$this->previous_exception_handler = set_exception_handler( array( $this, 'exception_handler' ) );
|
||||
|
||||
$this->error_reporting = error_reporting();
|
||||
$this->display_errors = ini_get( 'display_errors' );
|
||||
ini_set( 'display_errors', '0' );
|
||||
|
||||
if ( $prior_error ) {
|
||||
$this->error_handler(
|
||||
$prior_error['type'],
|
||||
$prior_error['message'],
|
||||
$prior_error['file'],
|
||||
$prior_error['line'],
|
||||
null,
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function tear_down() {
|
||||
if ( defined( 'QM_DISABLE_ERROR_HANDLER' ) && QM_DISABLE_ERROR_HANDLER ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( null !== $this->previous_error_handler ) {
|
||||
restore_error_handler();
|
||||
}
|
||||
|
||||
if ( null !== $this->previous_exception_handler ) {
|
||||
restore_exception_handler();
|
||||
}
|
||||
|
||||
if ( null !== $this->error_reporting ) {
|
||||
error_reporting( $this->error_reporting );
|
||||
}
|
||||
|
||||
if ( false !== $this->display_errors ) {
|
||||
ini_set( 'display_errors', $this->display_errors );
|
||||
}
|
||||
|
||||
parent::tear_down();
|
||||
}
|
||||
|
||||
/**
|
||||
* Uncaught error handler.
|
||||
*
|
||||
* @param Throwable $e The error or exception.
|
||||
* @return void
|
||||
*/
|
||||
public function exception_handler( $e ) {
|
||||
$error = 'Uncaught Error';
|
||||
|
||||
if ( $e instanceof Exception ) {
|
||||
$error = 'Uncaught Exception';
|
||||
}
|
||||
|
||||
$this->output_fatal( 'Fatal error', array(
|
||||
'message' => sprintf(
|
||||
'%s: %s',
|
||||
$error,
|
||||
$e->getMessage()
|
||||
),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'trace' => $e->getTrace(),
|
||||
) );
|
||||
|
||||
// The error must be re-thrown or passed to the previously registered exception handler so that the error
|
||||
// is logged appropriately instead of discarded silently.
|
||||
if ( $this->previous_exception_handler ) {
|
||||
call_user_func( $this->previous_exception_handler, $e );
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
exit( 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $errno The error number.
|
||||
* @param string $message The error message.
|
||||
* @param string $file The file location.
|
||||
* @param int $line The line number.
|
||||
* @param mixed[] $context The context being passed.
|
||||
* @param bool $do_trace Whether a stack trace should be included in the logged error data.
|
||||
* @return bool
|
||||
*/
|
||||
public function error_handler( $errno, $message, $file = null, $line = null, $context = null, $do_trace = true ) {
|
||||
$type = null;
|
||||
|
||||
/**
|
||||
* Fires before logging the PHP error in Query Monitor.
|
||||
*
|
||||
* @since 2.7.0
|
||||
*
|
||||
* @param int $errno The error number.
|
||||
* @param string $message The error message.
|
||||
* @param string|null $file The file location.
|
||||
* @param int|null $line The line number.
|
||||
* @param mixed[]|null $context The context being passed.
|
||||
*/
|
||||
do_action( 'qm/collect/new_php_error', $errno, $message, $file, $line, $context );
|
||||
|
||||
switch ( $errno ) {
|
||||
|
||||
case E_WARNING:
|
||||
case E_USER_WARNING:
|
||||
$type = 'warning';
|
||||
break;
|
||||
|
||||
case E_NOTICE:
|
||||
case E_USER_NOTICE:
|
||||
$type = 'notice';
|
||||
break;
|
||||
|
||||
case E_STRICT:
|
||||
$type = 'strict';
|
||||
break;
|
||||
|
||||
case E_DEPRECATED:
|
||||
case E_USER_DEPRECATED:
|
||||
$type = 'deprecated';
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if ( null === $type ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! class_exists( 'QM_Backtrace' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$error_group = 'errors';
|
||||
|
||||
if ( 0 === error_reporting() && 0 !== $this->error_reporting ) {
|
||||
// This is most likely an @-suppressed error
|
||||
$error_group = 'suppressed';
|
||||
}
|
||||
|
||||
if ( ! isset( self::$unexpected_error ) ) {
|
||||
// These strings are from core. They're passed through `__()` as variables so they get translated at runtime
|
||||
// but do not get seen by GlotPress when it populates its database of translatable strings for QM.
|
||||
$unexpected_error = 'An unexpected error occurred. Something may be wrong with WordPress.org or this server’s configuration. If you continue to have problems, please try the <a href="%s">support forums</a>.';
|
||||
$wordpress_forums = 'https://wordpress.org/support/forums/';
|
||||
|
||||
self::$unexpected_error = sprintf(
|
||||
call_user_func( '__', $unexpected_error ),
|
||||
call_user_func( '__', $wordpress_forums )
|
||||
);
|
||||
}
|
||||
|
||||
// Intentionally skip reporting these core warnings. They're a distraction when developing offline.
|
||||
// The failed HTTP request will still appear in QM's output so it's not a big problem hiding these warnings.
|
||||
if ( false !== strpos( $message, self::$unexpected_error ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$trace = new QM_Backtrace();
|
||||
$trace->push_frame( array(
|
||||
'file' => $file,
|
||||
'line' => $line,
|
||||
) );
|
||||
$caller = $trace->get_caller();
|
||||
|
||||
if ( $caller ) {
|
||||
$key = md5( $message . $file . $line . $caller['id'] );
|
||||
} else {
|
||||
$key = md5( $message . $file . $line );
|
||||
}
|
||||
|
||||
if ( isset( $this->data->{$error_group}[ $type ][ $key ] ) ) {
|
||||
$this->data->{$error_group}[ $type ][ $key ]['calls']++;
|
||||
} else {
|
||||
$this->data->{$error_group}[ $type ][ $key ] = array(
|
||||
'errno' => $errno,
|
||||
'type' => $type,
|
||||
'message' => wp_strip_all_tags( $message ),
|
||||
'file' => $file,
|
||||
'filename' => ( $file ? QM_Util::standard_dir( $file, '' ) : '' ),
|
||||
'line' => $line,
|
||||
'filtered_trace' => ( $do_trace ? $trace->get_filtered_trace() : null ),
|
||||
'component' => $trace->get_component(),
|
||||
'calls' => 1,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the PHP error handler return value. This can be used to control whether or not the default error
|
||||
* handler is called after Query Monitor's.
|
||||
*
|
||||
* @since 2.7.0
|
||||
*
|
||||
* @param bool $return_value Error handler return value. Default false.
|
||||
*/
|
||||
return apply_filters( 'qm/collect/php_errors_return_value', false );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $error
|
||||
* @param mixed[] $e
|
||||
* @phpstan-param array{
|
||||
* message: string,
|
||||
* file: string,
|
||||
* line: int,
|
||||
* type?: int,
|
||||
* trace?: mixed|null,
|
||||
* } $e
|
||||
* @return void
|
||||
*/
|
||||
protected function output_fatal( $error, array $e ) {
|
||||
$dispatcher = QM_Dispatchers::get( 'html' );
|
||||
|
||||
if ( empty( $dispatcher ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( empty( $this->display_errors ) && ! $dispatcher::user_can_view() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This hides the subsequent message from the fatal error handler in core. It cannot be
|
||||
// disabled by a plugin so we'll just hide its output.
|
||||
echo '<style type="text/css"> .wp-die-message { display: none; } </style>';
|
||||
|
||||
printf(
|
||||
// phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet
|
||||
'<link rel="stylesheet" href="%1$s?ver=%2$s" media="all" />',
|
||||
esc_url( QueryMonitor::init()->plugin_url( 'assets/query-monitor.css' ) ),
|
||||
esc_attr( QM_VERSION )
|
||||
);
|
||||
|
||||
// This unused wrapper with an attribute serves to help the #qm-fatal div break out of an
|
||||
// attribute if a fatal has occurred within one.
|
||||
echo '<div data-qm="qm">';
|
||||
|
||||
printf(
|
||||
'<div id="qm-fatal" data-qm-message="%1$s" data-qm-file="%2$s" data-qm-line="%3$d">',
|
||||
esc_attr( $e['message'] ),
|
||||
esc_attr( QM_Util::standard_dir( $e['file'], '' ) ),
|
||||
intval( $e['line'] )
|
||||
);
|
||||
|
||||
echo '<div class="qm-fatal-wrap">';
|
||||
|
||||
if ( QM_Output_Html::has_clickable_links() ) {
|
||||
$file = QM_Output_Html::output_filename( $e['file'], $e['file'], $e['line'], true );
|
||||
} else {
|
||||
$file = esc_html( $e['file'] );
|
||||
}
|
||||
|
||||
$warning = QueryMonitor::icon( 'warning' );
|
||||
|
||||
printf(
|
||||
'<p>%1$s <b>%2$s</b>: %3$s<br>in <b>%4$s</b> on line <b>%5$d</b></p>',
|
||||
$warning,
|
||||
esc_html( $error ),
|
||||
nl2br( esc_html( $e['message'] ), false ),
|
||||
$file,
|
||||
intval( $e['line'] )
|
||||
); // WPCS: XSS ok.
|
||||
|
||||
if ( ! empty( $e['trace'] ) ) {
|
||||
echo '<p>Call stack:</p>';
|
||||
echo '<ol>';
|
||||
foreach ( $e['trace'] as $frame ) {
|
||||
$callback = QM_Util::populate_callback( $frame );
|
||||
|
||||
if ( ! isset( $callback['name'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
printf(
|
||||
'<li>%s</li>',
|
||||
QM_Output_Html::output_filename( $callback['name'], $frame['file'], $frame['line'] )
|
||||
); // WPCS: XSS ok.
|
||||
}
|
||||
echo '</ol>';
|
||||
}
|
||||
|
||||
echo '</div>';
|
||||
|
||||
echo '<h2>Query Monitor</h2>';
|
||||
|
||||
echo '</div>';
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs post-processing on the collected errors and updates the
|
||||
* errors collected in the data->errors property.
|
||||
*
|
||||
* Any unreportable errors are placed in the data->filtered_errors
|
||||
* property.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process() {
|
||||
$this->types = array(
|
||||
'errors' => array(
|
||||
'warning' => _x( 'Warning', 'PHP error level', 'query-monitor' ),
|
||||
'notice' => _x( 'Notice', 'PHP error level', 'query-monitor' ),
|
||||
'strict' => _x( 'Strict', 'PHP error level', 'query-monitor' ),
|
||||
'deprecated' => _x( 'Deprecated', 'PHP error level', 'query-monitor' ),
|
||||
),
|
||||
'suppressed' => array(
|
||||
'warning' => _x( 'Warning (Suppressed)', 'Suppressed PHP error level', 'query-monitor' ),
|
||||
'notice' => _x( 'Notice (Suppressed)', 'Suppressed PHP error level', 'query-monitor' ),
|
||||
'strict' => _x( 'Strict (Suppressed)', 'Suppressed PHP error level', 'query-monitor' ),
|
||||
'deprecated' => _x( 'Deprecated (Suppressed)', 'Suppressed PHP error level', 'query-monitor' ),
|
||||
),
|
||||
'silenced' => array(
|
||||
'warning' => _x( 'Warning (Silenced)', 'Silenced PHP error level', 'query-monitor' ),
|
||||
'notice' => _x( 'Notice (Silenced)', 'Silenced PHP error level', 'query-monitor' ),
|
||||
'strict' => _x( 'Strict (Silenced)', 'Silenced PHP error level', 'query-monitor' ),
|
||||
'deprecated' => _x( 'Deprecated (Silenced)', 'Silenced PHP error level', 'query-monitor' ),
|
||||
),
|
||||
);
|
||||
$components = array();
|
||||
|
||||
if ( ! empty( $this->data->errors ) ) {
|
||||
/**
|
||||
* Filters the levels used for reported PHP errors on a per-component basis.
|
||||
*
|
||||
* Error levels can be specified in order to silence certain error levels from
|
||||
* plugins or the current theme. Most commonly, you may wish to use this filter
|
||||
* in order to silence annoying notices from third party plugins that you do not
|
||||
* have control over.
|
||||
*
|
||||
* Silenced errors will still appear in Query Monitor's output, but will not
|
||||
* cause highlighting to appear in the top level admin toolbar.
|
||||
*
|
||||
* For example, to show all errors in the 'foo' plugin except PHP notices use:
|
||||
*
|
||||
* add_filter( 'qm/collect/php_error_levels', function( array $levels ) {
|
||||
* $levels['plugin']['foo'] = ( E_ALL & ~E_NOTICE );
|
||||
* return $levels;
|
||||
* } );
|
||||
*
|
||||
* Errors from themes, WordPress core, and other components can also be filtered:
|
||||
*
|
||||
* add_filter( 'qm/collect/php_error_levels', function( array $levels ) {
|
||||
* $levels['theme']['stylesheet'] = ( E_WARNING & E_USER_WARNING );
|
||||
* $levels['theme']['template'] = ( E_WARNING & E_USER_WARNING );
|
||||
* $levels['core']['core'] = ( 0 );
|
||||
* return $levels;
|
||||
* } );
|
||||
*
|
||||
* Any component which doesn't have an error level specified via this filter is
|
||||
* assumed to have the default level of `E_ALL`, which shows all errors.
|
||||
*
|
||||
* Valid PHP error level bitmasks are supported for each component, including `0`
|
||||
* to silence all errors from a component. See the PHP documentation on error
|
||||
* reporting for more info: http://php.net/manual/en/function.error-reporting.php
|
||||
*
|
||||
* @since 2.7.0
|
||||
*
|
||||
* @param array<string,array<string,int>> $levels The error levels used for each component.
|
||||
*/
|
||||
$levels = apply_filters( 'qm/collect/php_error_levels', array() );
|
||||
|
||||
array_map( array( $this, 'filter_reportable_errors' ), $levels, array_keys( $levels ) );
|
||||
|
||||
foreach ( $this->types as $error_group => $error_types ) {
|
||||
foreach ( $error_types as $type => $title ) {
|
||||
if ( isset( $this->data->{$error_group}[ $type ] ) ) {
|
||||
/**
|
||||
* @var array<string, mixed> $error
|
||||
* @phpstan-var errorObject $error
|
||||
*/
|
||||
foreach ( $this->data->{$error_group}[ $type ] as $error ) {
|
||||
$components[ $error['component']->name ] = $error['component']->name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->data->components = $components;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the reportable PHP errors using the table specified. Users can customize the levels
|
||||
* using the `qm/collect/php_error_levels` filter.
|
||||
*
|
||||
* @param array<string, int> $components The error levels keyed by component name.
|
||||
* @param string $component_type The component type, for example 'plugin' or 'theme'.
|
||||
* @return void
|
||||
*/
|
||||
public function filter_reportable_errors( array $components, $component_type ) {
|
||||
$all_errors = $this->data->errors;
|
||||
|
||||
foreach ( $components as $component_context => $allowed_level ) {
|
||||
foreach ( $all_errors as $error_level => $errors ) {
|
||||
foreach ( $errors as $error_id => $error ) {
|
||||
if ( $this->is_reportable_error( $error['errno'], $allowed_level ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! $this->is_affected_component( $error['component'], $component_type, $component_context ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
unset( $this->data->errors[ $error_level ][ $error_id ] );
|
||||
|
||||
$this->data->silenced[ $error_level ][ $error_id ] = $error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->data->errors = array_filter( $this->data->errors );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the component is of the given type and has the given context. This is
|
||||
* used to scope an error to a plugin or theme.
|
||||
*
|
||||
* @param QM_Component $component The component.
|
||||
* @param string $component_type The component type for comparison.
|
||||
* @param string $component_context The component context for comparison.
|
||||
* @return bool
|
||||
*/
|
||||
public function is_affected_component( $component, $component_type, $component_context ) {
|
||||
return ( $component->type === $component_type && $component->context === $component_context );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the error number specified is viewable based on the
|
||||
* flags specified.
|
||||
*
|
||||
* Eg:- If a plugin had the config flags,
|
||||
*
|
||||
* E_ALL & ~E_NOTICE
|
||||
*
|
||||
* then,
|
||||
*
|
||||
* is_reportable_error( E_NOTICE, E_ALL & ~E_NOTICE ) is false
|
||||
* is_reportable_error( E_WARNING, E_ALL & ~E_NOTICE ) is true
|
||||
*
|
||||
* If the `$flag` is null, all errors are assumed to be
|
||||
* reportable by default.
|
||||
*
|
||||
* @param int $error_no The errno from PHP
|
||||
* @param int|null $flags The config flags specified by users
|
||||
* @return bool Whether the error is reportable.
|
||||
*/
|
||||
public function is_reportable_error( $error_no, $flags ) {
|
||||
$result = true;
|
||||
|
||||
if ( null !== $flags ) {
|
||||
$result = (bool) ( $error_no & $flags );
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* For testing purposes only. Sets the errors property manually.
|
||||
* Needed to test the filter since the data property is protected.
|
||||
*
|
||||
* @param array<string, mixed> $errors The list of errors
|
||||
* @return void
|
||||
*/
|
||||
public function set_php_errors( $errors ) {
|
||||
$this->data->errors = $errors;
|
||||
}
|
||||
}
|
||||
|
||||
# Load early to catch early errors
|
||||
QM_Collectors::add( new QM_Collector_PHP_Errors() );
|
97
wp-content/plugins/query-monitor/collectors/raw_request.php
Normal file
97
wp-content/plugins/query-monitor/collectors/raw_request.php
Normal file
@ -0,0 +1,97 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @extends QM_DataCollector<QM_Data_Raw_Request>
|
||||
*/
|
||||
class QM_Collector_Raw_Request extends QM_DataCollector {
|
||||
|
||||
public $id = 'raw_request';
|
||||
|
||||
public function get_storage(): QM_Data {
|
||||
return new QM_Data_Raw_Request();
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts headers from a PHP-style $_SERVER array.
|
||||
*
|
||||
* From WP_REST_Server::get_headers()
|
||||
*
|
||||
* @param array<string, string> $server Associative array similar to `$_SERVER`.
|
||||
* @return array<string, string> Headers extracted from the input.
|
||||
*/
|
||||
protected function get_headers( array $server ) {
|
||||
$headers = array();
|
||||
|
||||
// CONTENT_* headers are not prefixed with HTTP_.
|
||||
$additional = array(
|
||||
'CONTENT_LENGTH' => true,
|
||||
'CONTENT_MD5' => true,
|
||||
'CONTENT_TYPE' => true,
|
||||
);
|
||||
|
||||
foreach ( $server as $key => $value ) {
|
||||
if ( strpos( $key, 'HTTP_' ) === 0 ) {
|
||||
$headers[ substr( $key, 5 ) ] = $value;
|
||||
} elseif ( isset( $additional[ $key ] ) ) {
|
||||
$headers[ $key ] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process request and response data.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process() {
|
||||
$request = array(
|
||||
'ip' => $_SERVER['REMOTE_ADDR'],
|
||||
'method' => strtoupper( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ),
|
||||
'scheme' => is_ssl() ? 'https' : 'http',
|
||||
'host' => wp_unslash( $_SERVER['HTTP_HOST'] ),
|
||||
'path' => wp_unslash( $_SERVER['REQUEST_URI'] ?? '/' ),
|
||||
'query' => wp_unslash( $_SERVER['QUERY_STRING'] ?? '' ),
|
||||
'headers' => $this->get_headers( wp_unslash( $_SERVER ) ),
|
||||
);
|
||||
|
||||
ksort( $request['headers'] );
|
||||
|
||||
$request['url'] = sprintf( '%s://%s%s', $request['scheme'], $request['host'], $request['path'] );
|
||||
|
||||
$this->data->request = $request;
|
||||
|
||||
$headers = array();
|
||||
$raw_headers = headers_list();
|
||||
foreach ( $raw_headers as $row ) {
|
||||
list( $key, $value ) = explode( ':', $row, 2 );
|
||||
$headers[ trim( $key ) ] = trim( $value );
|
||||
}
|
||||
|
||||
ksort( $headers );
|
||||
|
||||
$response = array(
|
||||
'status' => http_response_code(),
|
||||
'headers' => $headers,
|
||||
);
|
||||
|
||||
$this->data->response = $response;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, QM_Collector> $collectors
|
||||
* @param QueryMonitor $qm
|
||||
* @return array<string, QM_Collector>
|
||||
*/
|
||||
function register_qm_collector_raw_request( array $collectors, QueryMonitor $qm ) {
|
||||
$collectors['raw_request'] = new QM_Collector_Raw_Request();
|
||||
return $collectors;
|
||||
}
|
||||
|
||||
add_filter( 'qm/collectors', 'register_qm_collector_raw_request', 10, 2 );
|
71
wp-content/plugins/query-monitor/collectors/redirects.php
Normal file
71
wp-content/plugins/query-monitor/collectors/redirects.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* HTTP redirect collector.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @extends QM_DataCollector<QM_Data_Redirect>
|
||||
*/
|
||||
class QM_Collector_Redirects extends QM_DataCollector {
|
||||
|
||||
public $id = 'redirects';
|
||||
|
||||
public function get_storage(): QM_Data {
|
||||
return new QM_Data_Redirect();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function set_up() {
|
||||
parent::set_up();
|
||||
add_filter( 'wp_redirect', array( $this, 'filter_wp_redirect' ), 9999, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function tear_down() {
|
||||
remove_filter( 'wp_redirect', array( $this, 'filter_wp_redirect' ), 9999 );
|
||||
|
||||
parent::tear_down();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $location
|
||||
* @param int $status
|
||||
* @return string
|
||||
*/
|
||||
public function filter_wp_redirect( $location, $status ) {
|
||||
|
||||
if ( ! $location ) {
|
||||
return $location;
|
||||
}
|
||||
|
||||
$trace = new QM_Backtrace( array(
|
||||
'ignore_hook' => array(
|
||||
current_filter() => true,
|
||||
),
|
||||
'ignore_func' => array(
|
||||
'wp_redirect' => true,
|
||||
),
|
||||
) );
|
||||
|
||||
$this->data->trace = $trace;
|
||||
$this->data->location = $location;
|
||||
$this->data->status = $status;
|
||||
|
||||
return $location;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Load early in case a plugin is doing a redirect when it initialises instead of after the `plugins_loaded` hook
|
||||
QM_Collectors::add( new QM_Collector_Redirects() );
|
332
wp-content/plugins/query-monitor/collectors/request.php
Normal file
332
wp-content/plugins/query-monitor/collectors/request.php
Normal file
@ -0,0 +1,332 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Request collector.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @extends QM_DataCollector<QM_Data_Request>
|
||||
*/
|
||||
class QM_Collector_Request extends QM_DataCollector {
|
||||
|
||||
public $id = 'request';
|
||||
|
||||
public function get_storage(): QM_Data {
|
||||
return new QM_Data_Request();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_concerned_actions() {
|
||||
return array(
|
||||
# Rewrites
|
||||
'generate_rewrite_rules',
|
||||
|
||||
# Everything else
|
||||
'parse_query',
|
||||
'parse_request',
|
||||
'parse_tax_query',
|
||||
'pre_get_posts',
|
||||
'send_headers',
|
||||
'the_post',
|
||||
'wp',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_concerned_filters() {
|
||||
global $wp_rewrite;
|
||||
|
||||
$filters = array(
|
||||
# Rewrite rules
|
||||
'author_rewrite_rules',
|
||||
'category_rewrite_rules',
|
||||
'comments_rewrite_rules',
|
||||
'date_rewrite_rules',
|
||||
'page_rewrite_rules',
|
||||
'post_format_rewrite_rules',
|
||||
'post_rewrite_rules',
|
||||
'root_rewrite_rules',
|
||||
'search_rewrite_rules',
|
||||
'tag_rewrite_rules',
|
||||
|
||||
# Home URL
|
||||
'home_url',
|
||||
|
||||
# Post permalinks
|
||||
'_get_page_link',
|
||||
'attachment_link',
|
||||
'page_link',
|
||||
'post_link',
|
||||
'post_type_link',
|
||||
'pre_post_link',
|
||||
'preview_post_link',
|
||||
'the_permalink',
|
||||
|
||||
# Post type archive permalinks
|
||||
'post_type_archive_link',
|
||||
|
||||
# Term permalinks
|
||||
'category_link',
|
||||
'pre_term_link',
|
||||
'tag_link',
|
||||
'term_link',
|
||||
|
||||
# User permalinks
|
||||
'author_link',
|
||||
|
||||
# Comment permalinks
|
||||
'get_comment_link',
|
||||
|
||||
# More rewrite stuff
|
||||
'iis7_url_rewrite_rules',
|
||||
'mod_rewrite_rules',
|
||||
'rewrite_rules',
|
||||
'rewrite_rules_array',
|
||||
|
||||
# Everything else
|
||||
'do_parse_request',
|
||||
'pre_handle_404',
|
||||
'query_string',
|
||||
'query_vars',
|
||||
'redirect_canonical',
|
||||
'request',
|
||||
'wp_headers',
|
||||
);
|
||||
|
||||
foreach ( $wp_rewrite->extra_permastructs as $permastructname => $struct ) {
|
||||
$filters[] = sprintf(
|
||||
'%s_rewrite_rules',
|
||||
$permastructname
|
||||
);
|
||||
}
|
||||
|
||||
return $filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_concerned_options() {
|
||||
return array(
|
||||
'home',
|
||||
'permalink_structure',
|
||||
'rewrite_rules',
|
||||
'siteurl',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_concerned_constants() {
|
||||
return array(
|
||||
'WP_HOME',
|
||||
'WP_SITEURL',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function process() {
|
||||
|
||||
global $wp, $wp_query, $current_blog, $current_site, $wp_rewrite;
|
||||
|
||||
$qo = get_queried_object();
|
||||
$user = wp_get_current_user();
|
||||
|
||||
if ( $user->exists() ) {
|
||||
$user_title = sprintf(
|
||||
/* translators: %d: User ID */
|
||||
__( 'Current User: #%d', 'query-monitor' ),
|
||||
$user->ID
|
||||
);
|
||||
} else {
|
||||
/* translators: No user */
|
||||
$user_title = _x( 'None', 'user', 'query-monitor' );
|
||||
}
|
||||
|
||||
$this->data->user = array(
|
||||
'title' => $user_title,
|
||||
'data' => ( $user->exists() ? $user : false ),
|
||||
);
|
||||
|
||||
if ( is_multisite() ) {
|
||||
$this->data->multisite['current_site'] = array(
|
||||
'title' => sprintf(
|
||||
/* translators: %d: Multisite site ID */
|
||||
__( 'Current Site: #%d', 'query-monitor' ),
|
||||
$current_blog->blog_id
|
||||
),
|
||||
'data' => $current_blog,
|
||||
);
|
||||
}
|
||||
|
||||
if ( QM_Util::is_multi_network() ) {
|
||||
$this->data->multisite['current_network'] = array(
|
||||
'title' => sprintf(
|
||||
/* translators: %d: Multisite network ID */
|
||||
__( 'Current Network: #%d', 'query-monitor' ),
|
||||
$current_site->id
|
||||
),
|
||||
'data' => $current_site,
|
||||
);
|
||||
}
|
||||
|
||||
if ( is_admin() ) {
|
||||
if ( isset( $_SERVER['REQUEST_URI'] ) ) {
|
||||
$path = parse_url( home_url(), PHP_URL_PATH );
|
||||
$home_path = trim( $path ?: '', '/' );
|
||||
$request = wp_unslash( $_SERVER['REQUEST_URI'] ); // phpcs:ignore
|
||||
|
||||
$this->data->request['request'] = str_replace( "/{$home_path}/", '', $request );
|
||||
} else {
|
||||
$this->data->request['request'] = '';
|
||||
}
|
||||
foreach ( array( 'query_string' ) as $item ) {
|
||||
$this->data->request[ $item ] = $wp->$item;
|
||||
}
|
||||
} else {
|
||||
foreach ( array( 'request', 'matched_rule', 'matched_query', 'query_string' ) as $item ) {
|
||||
$this->data->request[ $item ] = $wp->$item;
|
||||
}
|
||||
}
|
||||
|
||||
/** This filter is documented in wp-includes/class-wp.php */
|
||||
$plugin_qvars = array_flip( apply_filters( 'query_vars', array() ) );
|
||||
|
||||
/** @var array<string, mixed> */
|
||||
$qvars = $wp_query->query_vars;
|
||||
$query_vars = array();
|
||||
|
||||
foreach ( $qvars as $k => $v ) {
|
||||
if ( isset( $plugin_qvars[ $k ] ) ) {
|
||||
if ( '' !== $v ) {
|
||||
$query_vars[ $k ] = $v;
|
||||
}
|
||||
} else {
|
||||
if ( ! empty( $v ) ) {
|
||||
$query_vars[ $k ] = $v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ksort( $query_vars );
|
||||
|
||||
# First add plugin vars to $this->data->qvars:
|
||||
foreach ( $query_vars as $k => $v ) {
|
||||
if ( isset( $plugin_qvars[ $k ] ) ) {
|
||||
$this->data->qvars[ $k ] = $v;
|
||||
$this->data->plugin_qvars[ $k ] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
# Now add all other vars to $this->data->qvars:
|
||||
foreach ( $query_vars as $k => $v ) {
|
||||
if ( ! isset( $plugin_qvars[ $k ] ) ) {
|
||||
$this->data->qvars[ $k ] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
switch ( true ) {
|
||||
|
||||
case ! is_object( $qo ):
|
||||
// Nada
|
||||
break;
|
||||
|
||||
case is_a( $qo, 'WP_Post' ):
|
||||
// Single post
|
||||
$this->data->queried_object['title'] = sprintf(
|
||||
/* translators: 1: Post type name, 2: Post ID */
|
||||
__( 'Single %1$s: #%2$d', 'query-monitor' ),
|
||||
get_post_type_object( $qo->post_type )->labels->singular_name,
|
||||
$qo->ID
|
||||
);
|
||||
break;
|
||||
|
||||
case is_a( $qo, 'WP_User' ):
|
||||
// Author archive
|
||||
$this->data->queried_object['title'] = sprintf(
|
||||
/* translators: %s: Author name */
|
||||
__( 'Author archive: %s', 'query-monitor' ),
|
||||
$qo->user_nicename
|
||||
);
|
||||
break;
|
||||
|
||||
case is_a( $qo, 'WP_Term' ):
|
||||
case property_exists( $qo, 'slug' ):
|
||||
// Term archive
|
||||
$this->data->queried_object['title'] = sprintf(
|
||||
/* translators: %s: Taxonomy term name */
|
||||
__( 'Term archive: %s', 'query-monitor' ),
|
||||
$qo->slug
|
||||
);
|
||||
break;
|
||||
|
||||
case is_a( $qo, 'WP_Post_Type' ):
|
||||
case property_exists( $qo, 'has_archive' ):
|
||||
// Post type archive
|
||||
$this->data->queried_object['title'] = sprintf(
|
||||
/* translators: %s: Post type name */
|
||||
__( 'Post type archive: %s', 'query-monitor' ),
|
||||
$qo->name
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
// Unknown, but we have a queried object
|
||||
$this->data->queried_object['title'] = __( 'Unknown queried object', 'query-monitor' );
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if ( $qo ) {
|
||||
$this->data->queried_object['data'] = $qo;
|
||||
}
|
||||
|
||||
if ( isset( $_SERVER['REQUEST_METHOD'] ) ) {
|
||||
$this->data->request_method = strtoupper( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ); // phpcs:ignore
|
||||
} else {
|
||||
$this->data->request_method = '';
|
||||
}
|
||||
|
||||
if ( is_admin() || QM_Util::is_async() || empty( $wp_rewrite->rules ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$matching = array();
|
||||
|
||||
/** @var array<string, string> */
|
||||
$rewrite_rules = $wp_rewrite->rules;
|
||||
|
||||
foreach ( $rewrite_rules as $match => $query ) {
|
||||
if ( preg_match( "#^{$match}#", $this->data->request['request'] ) ) {
|
||||
$matching[ $match ] = $query;
|
||||
}
|
||||
}
|
||||
|
||||
$this->data->matching_rewrites = $matching;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, QM_Collector> $collectors
|
||||
* @param QueryMonitor $qm
|
||||
* @return array<string, QM_Collector>
|
||||
*/
|
||||
function register_qm_collector_request( array $collectors, QueryMonitor $qm ) {
|
||||
$collectors['request'] = new QM_Collector_Request();
|
||||
return $collectors;
|
||||
}
|
||||
|
||||
add_filter( 'qm/collectors', 'register_qm_collector_request', 10, 2 );
|
594
wp-content/plugins/query-monitor/collectors/theme.php
Normal file
594
wp-content/plugins/query-monitor/collectors/theme.php
Normal file
@ -0,0 +1,594 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Template and theme collector.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @extends QM_DataCollector<QM_Data_Theme>
|
||||
*/
|
||||
class QM_Collector_Theme extends QM_DataCollector {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $id = 'response';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $got_theme_compat = false;
|
||||
|
||||
/**
|
||||
* @var array<int, mixed>
|
||||
*/
|
||||
protected $requested_template_parts = array();
|
||||
|
||||
/**
|
||||
* @var array<int, mixed>
|
||||
*/
|
||||
protected $requested_template_part_posts = array();
|
||||
|
||||
/**
|
||||
* @var array<int, mixed>
|
||||
*/
|
||||
protected $requested_template_part_files = array();
|
||||
|
||||
/**
|
||||
* @var array<int, mixed>
|
||||
*/
|
||||
protected $requested_template_part_nopes = array();
|
||||
|
||||
/**
|
||||
* @var ?WP_Block_Template
|
||||
*/
|
||||
protected $block_template = null;
|
||||
|
||||
public function get_storage(): QM_Data {
|
||||
return new QM_Data_Theme();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function set_up() {
|
||||
parent::set_up();
|
||||
|
||||
add_filter( 'body_class', array( $this, 'filter_body_class' ), 9999 );
|
||||
add_filter( 'timber/output', array( $this, 'filter_timber_output' ), 9999, 3 );
|
||||
add_action( 'template_redirect', array( $this, 'action_template_redirect' ) );
|
||||
add_action( 'get_template_part', array( $this, 'action_get_template_part' ), 10, 3 );
|
||||
add_action( 'get_header', array( $this, 'action_get_position' ) );
|
||||
add_action( 'get_sidebar', array( $this, 'action_get_position' ) );
|
||||
add_action( 'get_footer', array( $this, 'action_get_position' ) );
|
||||
add_action( 'render_block_core_template_part_post', array( $this, 'action_render_block_core_template_part_post' ), 10, 3 );
|
||||
add_action( 'render_block_core_template_part_file', array( $this, 'action_render_block_core_template_part_file' ), 10, 3 );
|
||||
add_action( 'render_block_core_template_part_none', array( $this, 'action_render_block_core_template_part_none' ), 10, 3 );
|
||||
add_action( 'gutenberg_render_block_core_template_part_post', array( $this, 'action_render_block_core_template_part_post' ), 10, 3 );
|
||||
add_action( 'gutenberg_render_block_core_template_part_file', array( $this, 'action_render_block_core_template_part_file' ), 10, 3 );
|
||||
add_action( 'gutenberg_render_block_core_template_part_none', array( $this, 'action_render_block_core_template_part_none' ), 10, 3 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function tear_down() {
|
||||
remove_filter( 'body_class', array( $this, 'filter_body_class' ), 9999 );
|
||||
remove_filter( 'timber/output', array( $this, 'filter_timber_output' ), 9999 );
|
||||
remove_action( 'template_redirect', array( $this, 'action_template_redirect' ) );
|
||||
remove_action( 'get_template_part', array( $this, 'action_get_template_part' ), 10 );
|
||||
remove_action( 'get_header', array( $this, 'action_get_position' ) );
|
||||
remove_action( 'get_sidebar', array( $this, 'action_get_position' ) );
|
||||
remove_action( 'get_footer', array( $this, 'action_get_position' ) );
|
||||
remove_action( 'render_block_core_template_part_post', array( $this, 'action_render_block_core_template_part_post' ), 10 );
|
||||
remove_action( 'render_block_core_template_part_file', array( $this, 'action_render_block_core_template_part_file' ), 10 );
|
||||
remove_action( 'render_block_core_template_part_none', array( $this, 'action_render_block_core_template_part_none' ), 10 );
|
||||
remove_action( 'gutenberg_render_block_core_template_part_post', array( $this, 'action_render_block_core_template_part_post' ), 10 );
|
||||
remove_action( 'gutenberg_render_block_core_template_part_file', array( $this, 'action_render_block_core_template_part_file' ), 10 );
|
||||
remove_action( 'gutenberg_render_block_core_template_part_none', array( $this, 'action_render_block_core_template_part_none' ), 10 );
|
||||
|
||||
parent::tear_down();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires before the header/sidebar/footer template file is loaded.
|
||||
*
|
||||
* @param string|null $name Name of the specific file to use. Null for the default.
|
||||
* @return void
|
||||
*/
|
||||
public function action_get_position( $name ) {
|
||||
$filter = current_filter();
|
||||
$trace = new QM_Backtrace( array(
|
||||
'ignore_hook' => array(
|
||||
$filter => true,
|
||||
),
|
||||
) );
|
||||
|
||||
$position = str_replace( 'get_', '', $filter );
|
||||
$templates = array();
|
||||
if ( '' !== (string) $name ) {
|
||||
$templates[] = "{$position}-{$name}.php";
|
||||
}
|
||||
|
||||
$templates[] = "{$position}.php";
|
||||
|
||||
$data = array(
|
||||
'slug' => $position,
|
||||
'name' => $name,
|
||||
'templates' => $templates,
|
||||
'caller' => $trace->get_caller(),
|
||||
);
|
||||
|
||||
$this->requested_template_parts[] = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_concerned_actions() {
|
||||
return array(
|
||||
'template_redirect',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_concerned_filters() {
|
||||
$filters = array(
|
||||
'stylesheet',
|
||||
'stylesheet_directory',
|
||||
'template',
|
||||
'template_directory',
|
||||
'template_include',
|
||||
);
|
||||
|
||||
foreach ( self::get_query_filter_names() as $filter ) {
|
||||
$filters[] = $filter;
|
||||
$filters[] = "{$filter}_hierarchy";
|
||||
}
|
||||
|
||||
return $filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function get_concerned_options() {
|
||||
return array(
|
||||
'stylesheet',
|
||||
'template',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int|string, string>
|
||||
*/
|
||||
public static function get_query_template_names() {
|
||||
$names = array();
|
||||
|
||||
$names['embed'] = 'is_embed';
|
||||
$names['404'] = 'is_404';
|
||||
$names['search'] = 'is_search';
|
||||
$names['front_page'] = 'is_front_page';
|
||||
$names['home'] = 'is_home';
|
||||
$names['privacy_policy'] = 'is_privacy_policy';
|
||||
$names['post_type_archive'] = 'is_post_type_archive';
|
||||
$names['taxonomy'] = 'is_tax';
|
||||
$names['attachment'] = 'is_attachment';
|
||||
$names['single'] = 'is_single';
|
||||
$names['page'] = 'is_page';
|
||||
$names['singular'] = 'is_singular';
|
||||
$names['category'] = 'is_category';
|
||||
$names['tag'] = 'is_tag';
|
||||
$names['author'] = 'is_author';
|
||||
$names['date'] = 'is_date';
|
||||
$names['archive'] = 'is_archive';
|
||||
$names['index'] = '__return_true';
|
||||
|
||||
return $names;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int|string, string>
|
||||
*/
|
||||
public static function get_query_filter_names() {
|
||||
$names = array();
|
||||
|
||||
$names['embed'] = 'embed_template';
|
||||
$names['404'] = '404_template';
|
||||
$names['search'] = 'search_template';
|
||||
$names['front_page'] = 'frontpage_template';
|
||||
$names['home'] = 'home_template';
|
||||
$names['privacy_policy'] = 'privacypolicy_template';
|
||||
$names['taxonomy'] = 'taxonomy_template';
|
||||
$names['attachment'] = 'attachment_template';
|
||||
$names['single'] = 'single_template';
|
||||
$names['page'] = 'page_template';
|
||||
$names['singular'] = 'singular_template';
|
||||
$names['category'] = 'category_template';
|
||||
$names['tag'] = 'tag_template';
|
||||
$names['author'] = 'author_template';
|
||||
$names['date'] = 'date_template';
|
||||
$names['archive'] = 'archive_template';
|
||||
$names['index'] = 'index_template';
|
||||
|
||||
return $names;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function action_template_redirect() {
|
||||
add_filter( 'template_include', array( $this, 'filter_template_include' ), PHP_INT_MAX );
|
||||
|
||||
foreach ( self::get_query_template_names() as $template => $conditional ) {
|
||||
// If a matching theme-compat file is found, further conditional checks won't occur in template-loader.php
|
||||
if ( $this->got_theme_compat ) {
|
||||
break;
|
||||
}
|
||||
|
||||
$get_template = "get_{$template}_template";
|
||||
|
||||
if ( function_exists( $conditional ) && function_exists( $get_template ) && call_user_func( $conditional ) ) {
|
||||
$filter = str_replace( '_', '', "{$template}" );
|
||||
add_filter( "{$filter}_template_hierarchy", array( $this, 'filter_template_hierarchy' ), PHP_INT_MAX );
|
||||
add_filter( "{$filter}_template", array( $this, 'filter_template' ), PHP_INT_MAX, 3 );
|
||||
call_user_func( $get_template );
|
||||
remove_filter( "{$filter}_template_hierarchy", array( $this, 'filter_template_hierarchy' ), PHP_INT_MAX );
|
||||
remove_filter( "{$filter}_template", array( $this, 'filter_template' ), PHP_INT_MAX );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires before a template part is loaded.
|
||||
*
|
||||
* @param string $slug The slug name for the generic template.
|
||||
* @param string $name The name of the specialized template or an empty
|
||||
* string if there is none.
|
||||
* @param array<int, string> $templates Array of template files to search for, in order.
|
||||
* @return void
|
||||
*/
|
||||
public function action_get_template_part( $slug, $name, $templates ) {
|
||||
$part = compact( 'slug', 'name', 'templates' );
|
||||
|
||||
$trace = new QM_Backtrace( array(
|
||||
'ignore_hook' => array(
|
||||
current_filter() => true,
|
||||
),
|
||||
) );
|
||||
|
||||
$part['caller'] = $trace->get_caller();
|
||||
|
||||
$this->requested_template_parts[] = $part;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires when a post is loaded for a template part block.
|
||||
*
|
||||
* @param string $template_part_id
|
||||
* @param mixed[] $attributes
|
||||
* @param WP_Post $post
|
||||
* @return void
|
||||
*/
|
||||
public function action_render_block_core_template_part_post( $template_part_id, $attributes, WP_Post $post ) {
|
||||
$part = array(
|
||||
'id' => $template_part_id,
|
||||
'attributes' => $attributes,
|
||||
'post' => $post->ID,
|
||||
);
|
||||
$this->requested_template_part_posts[] = $part;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires when a file is loaded for a template part block.
|
||||
*
|
||||
* @param string $template_part_id
|
||||
* @param mixed[] $attributes
|
||||
* @param string $template_part_file_path
|
||||
* @return void
|
||||
*/
|
||||
public function action_render_block_core_template_part_file( $template_part_id, $attributes, $template_part_file_path ) {
|
||||
$part = array(
|
||||
'id' => $template_part_id,
|
||||
'attributes' => $attributes,
|
||||
'path' => $template_part_file_path,
|
||||
);
|
||||
$this->requested_template_part_files[] = $part;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires when neither a post nor file is found for a template part block.
|
||||
*
|
||||
* @param string $template_part_id
|
||||
* @param mixed[] $attributes
|
||||
* @param string $template_part_file_path
|
||||
* @return void
|
||||
*/
|
||||
public function action_render_block_core_template_part_none( $template_part_id, $attributes, $template_part_file_path ) {
|
||||
$part = array(
|
||||
'id' => $template_part_id,
|
||||
'attributes' => $attributes,
|
||||
'path' => $template_part_file_path,
|
||||
);
|
||||
$this->requested_template_part_nopes[] = $part;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, string> $templates
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function filter_template_hierarchy( array $templates ) {
|
||||
if ( ! isset( $this->data->template_hierarchy ) ) {
|
||||
$this->data->template_hierarchy = array();
|
||||
}
|
||||
|
||||
foreach ( $templates as $template_name ) {
|
||||
if ( file_exists( ABSPATH . WPINC . '/theme-compat/' . $template_name ) ) {
|
||||
$this->got_theme_compat = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( self::wp_is_block_theme() ) {
|
||||
$block_theme_folders = self::wp_get_block_theme_folders();
|
||||
foreach ( $templates as $template ) {
|
||||
if ( str_ends_with( $template, '.php' ) ) {
|
||||
// Standard PHP template, inject the HTML version:
|
||||
$this->data->template_hierarchy[] = $block_theme_folders['wp_template'] . '/' . str_replace( '.php', '.html', $template );
|
||||
$this->data->template_hierarchy[] = $template;
|
||||
} else {
|
||||
// Block theme custom template (eg. from `customTemplates` in theme.json), doesn't have a suffix:
|
||||
$this->data->template_hierarchy[] = $block_theme_folders['wp_template'] . '/' . $template . '.html';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->data->template_hierarchy = array_merge( $this->data->template_hierarchy, $templates );
|
||||
}
|
||||
|
||||
return $templates;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $template Path to the template. See locate_template().
|
||||
* @param string $type Sanitized filename without extension.
|
||||
* @param array<int, string> $templates A list of template candidates, in descending order of priority.
|
||||
* @return string Full path to template file.
|
||||
*/
|
||||
public function filter_template( $template, $type, $templates ) {
|
||||
if ( $this->data->block_template instanceof \WP_Block_Template ) {
|
||||
return $template;
|
||||
}
|
||||
|
||||
$block_template = self::wp_resolve_block_template( $type, $templates, $template );
|
||||
|
||||
if ( $block_template ) {
|
||||
$this->data->block_template = $block_template;
|
||||
}
|
||||
|
||||
return $template;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, string> $class
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function filter_body_class( array $class ) {
|
||||
$this->data->body_class = $class;
|
||||
return $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $template_path
|
||||
* @return string
|
||||
*/
|
||||
public function filter_template_include( $template_path ) {
|
||||
$this->data->template_path = $template_path;
|
||||
return $template_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $output
|
||||
* @param mixed $data
|
||||
* @param string $file
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function filter_timber_output( $output, $data = null, $file = null ) {
|
||||
if ( $file ) {
|
||||
$this->data->timber_files[] = $file;
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function process() {
|
||||
|
||||
$stylesheet_directory = QM_Util::standard_dir( get_stylesheet_directory() );
|
||||
$template_directory = QM_Util::standard_dir( get_template_directory() );
|
||||
$theme_directory = QM_Util::standard_dir( get_theme_root() );
|
||||
|
||||
if ( isset( $this->data->template_hierarchy ) ) {
|
||||
$this->data->template_hierarchy = array_unique( $this->data->template_hierarchy );
|
||||
}
|
||||
|
||||
if ( ! empty( $this->requested_template_parts ) ) {
|
||||
$this->data->template_parts = array();
|
||||
$this->data->theme_template_parts = array();
|
||||
$this->data->count_template_parts = array();
|
||||
|
||||
foreach ( $this->requested_template_parts as $part ) {
|
||||
$file = locate_template( $part['templates'] );
|
||||
|
||||
if ( ! $file ) {
|
||||
$this->data->unsuccessful_template_parts[] = $part;
|
||||
continue;
|
||||
}
|
||||
|
||||
$file = QM_Util::standard_dir( $file );
|
||||
|
||||
if ( isset( $this->data->count_template_parts[ $file ] ) ) {
|
||||
$this->data->count_template_parts[ $file ]++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->data->count_template_parts[ $file ] = 1;
|
||||
|
||||
$filename = str_replace( array(
|
||||
$stylesheet_directory,
|
||||
$template_directory,
|
||||
), '', $file );
|
||||
|
||||
$display = trim( $filename, '/' );
|
||||
$theme_display = trim( str_replace( $theme_directory, '', $file ), '/' );
|
||||
|
||||
$this->data->template_parts[ $file ] = $display;
|
||||
$this->data->theme_template_parts[ $file ] = $theme_display;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
! empty( $this->requested_template_part_posts ) ||
|
||||
! empty( $this->requested_template_part_files ) ||
|
||||
! empty( $this->requested_template_part_nopes )
|
||||
) {
|
||||
$this->data->template_parts = array();
|
||||
$this->data->theme_template_parts = array();
|
||||
$this->data->count_template_parts = array();
|
||||
|
||||
$posts = ! empty( $this->requested_template_part_posts ) ? $this->requested_template_part_posts : array();
|
||||
$files = ! empty( $this->requested_template_part_files ) ? $this->requested_template_part_files : array();
|
||||
$nopes = ! empty( $this->requested_template_part_nopes ) ? $this->requested_template_part_nopes : array();
|
||||
|
||||
$all = array_merge( $posts, $files, $nopes );
|
||||
|
||||
foreach ( $all as $part ) {
|
||||
$file = $part['path'] ?? $part['post'];
|
||||
|
||||
if ( isset( $this->data->count_template_parts[ $file ] ) ) {
|
||||
$this->data->count_template_parts[ $file ]++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->data->count_template_parts[ $file ] = 1;
|
||||
|
||||
if ( isset( $part['post'] ) ) {
|
||||
$display = $part['id'];
|
||||
$theme_display = $display;
|
||||
} else {
|
||||
$file = QM_Util::standard_dir( $file );
|
||||
|
||||
$filename = str_replace( array(
|
||||
$stylesheet_directory,
|
||||
$template_directory,
|
||||
), '', $file );
|
||||
|
||||
$display = trim( $filename, '/' );
|
||||
$theme_display = trim( str_replace( $theme_directory, '', $file ), '/' );
|
||||
}
|
||||
|
||||
$this->data->template_parts[ $file ] = $display;
|
||||
$this->data->theme_template_parts[ $file ] = $theme_display;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $this->data->template_path ) ) {
|
||||
$template_path = QM_Util::standard_dir( $this->data->template_path );
|
||||
$template_file = str_replace( array( $stylesheet_directory, $template_directory, ABSPATH ), '', $template_path );
|
||||
$template_file = ltrim( $template_file, '/' );
|
||||
$theme_template_file = str_replace( array( $theme_directory, ABSPATH ), '', $template_path );
|
||||
$theme_template_file = ltrim( $theme_template_file, '/' );
|
||||
|
||||
$this->data->template_path = $template_path;
|
||||
$this->data->template_file = $template_file;
|
||||
$this->data->theme_template_file = $theme_template_file;
|
||||
}
|
||||
|
||||
$this->data->stylesheet = get_stylesheet();
|
||||
$this->data->template = get_template();
|
||||
$this->data->is_child_theme = ( $this->data->stylesheet !== $this->data->template );
|
||||
$this->data->theme_dirs = array(
|
||||
$this->data->stylesheet => $stylesheet_directory,
|
||||
$this->data->template => $template_directory,
|
||||
);
|
||||
|
||||
$this->data->theme_folders = self::wp_get_block_theme_folders();
|
||||
|
||||
$stylesheet_theme_json = $stylesheet_directory . '/theme.json';
|
||||
$template_theme_json = $template_directory . '/theme.json';
|
||||
|
||||
if ( is_readable( $stylesheet_theme_json ) ) {
|
||||
$this->data->stylesheet_theme_json = $stylesheet_theme_json;
|
||||
}
|
||||
|
||||
if ( is_readable( $template_theme_json ) ) {
|
||||
$this->data->template_theme_json = $template_theme_json;
|
||||
}
|
||||
|
||||
if ( isset( $this->data->body_class ) ) {
|
||||
asort( $this->data->body_class );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected static function wp_is_block_theme() {
|
||||
return function_exists( 'wp_is_block_theme' ) && wp_is_block_theme();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected static function wp_get_block_theme_folders() {
|
||||
if ( ! function_exists( 'get_block_theme_folders' ) ) {
|
||||
return array(
|
||||
'wp_template' => 'templates',
|
||||
'wp_template_part' => 'parts',
|
||||
);
|
||||
}
|
||||
|
||||
return get_block_theme_folders();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $template_type The current template type.
|
||||
* @param array<int, string> $template_hierarchy The current template hierarchy, ordered by priority.
|
||||
* @param string $fallback_template A PHP fallback template to use if no matching block template is found.
|
||||
* @return WP_Block_Template|null template A template object, or null if none could be found.
|
||||
*/
|
||||
protected static function wp_resolve_block_template( $template_type, $template_hierarchy, $fallback_template ) {
|
||||
if ( ! function_exists( 'resolve_block_template' ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( ! current_theme_supports( 'block-templates' ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return resolve_block_template( $template_type, $template_hierarchy, $fallback_template );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, QM_Collector> $collectors
|
||||
* @param QueryMonitor $qm
|
||||
* @return array<string, QM_Collector>
|
||||
*/
|
||||
function register_qm_collector_theme( array $collectors, QueryMonitor $qm ) {
|
||||
$collectors['response'] = new QM_Collector_Theme();
|
||||
return $collectors;
|
||||
}
|
||||
|
||||
if ( ! is_admin() ) {
|
||||
add_filter( 'qm/collectors', 'register_qm_collector_theme', 10, 2 );
|
||||
}
|
168
wp-content/plugins/query-monitor/collectors/timing.php
Normal file
168
wp-content/plugins/query-monitor/collectors/timing.php
Normal file
@ -0,0 +1,168 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Timing and profiling collector.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @extends QM_DataCollector<QM_Data_Timing>
|
||||
*/
|
||||
class QM_Collector_Timing extends QM_DataCollector {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $id = 'timing';
|
||||
|
||||
/**
|
||||
* @var array<string, QM_Timer>
|
||||
*/
|
||||
private $track_timer = array();
|
||||
|
||||
/**
|
||||
* @var array<string, QM_Timer>
|
||||
*/
|
||||
private $start = array();
|
||||
|
||||
/**
|
||||
* @var array<string, QM_Timer>
|
||||
*/
|
||||
private $stop = array();
|
||||
|
||||
public function get_storage(): QM_Data {
|
||||
return new QM_Data_Timing();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function set_up() {
|
||||
parent::set_up();
|
||||
|
||||
add_action( 'qm/start', array( $this, 'action_function_time_start' ), 10, 1 );
|
||||
add_action( 'qm/stop', array( $this, 'action_function_time_stop' ), 10, 1 );
|
||||
add_action( 'qm/lap', array( $this, 'action_function_time_lap' ), 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function tear_down() {
|
||||
remove_action( 'qm/start', array( $this, 'action_function_time_start' ), 10 );
|
||||
remove_action( 'qm/stop', array( $this, 'action_function_time_stop' ), 10 );
|
||||
remove_action( 'qm/lap', array( $this, 'action_function_time_lap' ), 10 );
|
||||
|
||||
parent::tear_down();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $function
|
||||
* @return void
|
||||
*/
|
||||
public function action_function_time_start( $function ) {
|
||||
$this->track_timer[ $function ] = new QM_Timer();
|
||||
$this->start[ $function ] = $this->track_timer[ $function ]->start();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $function
|
||||
* @return void
|
||||
*/
|
||||
public function action_function_time_stop( $function ) {
|
||||
if ( ! isset( $this->track_timer[ $function ] ) ) {
|
||||
$trace = new QM_Backtrace();
|
||||
$this->data->warning[] = array(
|
||||
'function' => $function,
|
||||
'message' => __( 'Timer not started', 'query-monitor' ),
|
||||
'filtered_trace' => $trace->get_filtered_trace(),
|
||||
'component' => $trace->get_component(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
$this->stop[ $function ] = $this->track_timer[ $function ]->stop();
|
||||
$this->calculate_time( $function );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $function
|
||||
* @param string $name
|
||||
* @return void
|
||||
*/
|
||||
public function action_function_time_lap( $function, $name = null ) {
|
||||
if ( ! isset( $this->track_timer[ $function ] ) ) {
|
||||
$trace = new QM_Backtrace();
|
||||
$this->data->warning[] = array(
|
||||
'function' => $function,
|
||||
'message' => __( 'Timer not started', 'query-monitor' ),
|
||||
'filtered_trace' => $trace->get_filtered_trace(),
|
||||
'component' => $trace->get_component(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
$this->track_timer[ $function ]->lap( array(), $name );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $function
|
||||
* @return void
|
||||
*/
|
||||
public function calculate_time( $function ) {
|
||||
$trace = $this->track_timer[ $function ]->get_trace();
|
||||
$function_time = $this->track_timer[ $function ]->get_time();
|
||||
$function_memory = $this->track_timer[ $function ]->get_memory();
|
||||
$function_laps = $this->track_timer[ $function ]->get_laps();
|
||||
$start_time = $this->track_timer[ $function ]->get_start_time();
|
||||
$end_time = $this->track_timer[ $function ]->get_end_time();
|
||||
|
||||
$this->data->timing[] = array(
|
||||
'function' => $function,
|
||||
'function_time' => $function_time,
|
||||
'function_memory' => $function_memory,
|
||||
'laps' => $function_laps,
|
||||
'filtered_trace' => $trace->get_filtered_trace(),
|
||||
'component' => $trace->get_component(),
|
||||
'start_time' => ( $start_time - $_SERVER['REQUEST_TIME_FLOAT'] ),
|
||||
'end_time' => ( $end_time - $_SERVER['REQUEST_TIME_FLOAT'] ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function process() {
|
||||
foreach ( $this->start as $function => $value ) {
|
||||
if ( ! isset( $this->stop[ $function ] ) ) {
|
||||
$trace = $this->track_timer[ $function ]->get_trace();
|
||||
$this->data->warning[] = array(
|
||||
'function' => $function,
|
||||
'message' => __( 'Timer not stopped', 'query-monitor' ),
|
||||
'filtered_trace' => $trace->get_filtered_trace(),
|
||||
'component' => $trace->get_component(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $this->data->timing ) ) {
|
||||
usort( $this->data->timing, array( $this, 'sort_by_start_time' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $a
|
||||
* @param mixed[] $b
|
||||
* @return int
|
||||
* @phpstan-return -1|0|1
|
||||
*/
|
||||
public function sort_by_start_time( array $a, array $b ) {
|
||||
return $a['start_time'] <=> $b['start_time'];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Load early in case a plugin is setting the function to be checked when it initialises instead of after the `plugins_loaded` hook
|
||||
QM_Collectors::add( new QM_Collector_Timing() );
|
111
wp-content/plugins/query-monitor/collectors/transients.php
Normal file
111
wp-content/plugins/query-monitor/collectors/transients.php
Normal file
@ -0,0 +1,111 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Transient storage collector.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @extends QM_DataCollector<QM_Data_Transients>
|
||||
*/
|
||||
class QM_Collector_Transients extends QM_DataCollector {
|
||||
|
||||
public $id = 'transients';
|
||||
|
||||
public function get_storage(): QM_Data {
|
||||
return new QM_Data_Transients();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function set_up() {
|
||||
parent::set_up();
|
||||
|
||||
add_action( 'setted_site_transient', array( $this, 'action_setted_site_transient' ), 10, 3 );
|
||||
add_action( 'setted_transient', array( $this, 'action_setted_blog_transient' ), 10, 3 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function tear_down() {
|
||||
remove_action( 'setted_site_transient', array( $this, 'action_setted_site_transient' ), 10 );
|
||||
remove_action( 'setted_transient', array( $this, 'action_setted_blog_transient' ), 10 );
|
||||
parent::tear_down();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $transient
|
||||
* @param mixed $value
|
||||
* @param int $expiration
|
||||
* @return void
|
||||
*/
|
||||
public function action_setted_site_transient( $transient, $value, $expiration ) {
|
||||
$this->setted_transient( $transient, 'site', $value, $expiration );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $transient
|
||||
* @param mixed $value
|
||||
* @param int $expiration
|
||||
* @return void
|
||||
*/
|
||||
public function action_setted_blog_transient( $transient, $value, $expiration ) {
|
||||
$this->setted_transient( $transient, 'blog', $value, $expiration );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $transient
|
||||
* @param string $type
|
||||
* @param mixed $value
|
||||
* @param int $expiration
|
||||
* @phpstan-param 'site'|'blog' $value
|
||||
* @return void
|
||||
*/
|
||||
public function setted_transient( $transient, $type, $value, $expiration ) {
|
||||
$trace = new QM_Backtrace( array(
|
||||
'ignore_hook' => array(
|
||||
current_filter() => true,
|
||||
),
|
||||
'ignore_func' => array(
|
||||
'set_transient' => true,
|
||||
'set_site_transient' => true,
|
||||
),
|
||||
) );
|
||||
|
||||
$name = str_replace( array(
|
||||
'_site_transient_',
|
||||
'_transient_',
|
||||
), '', $transient );
|
||||
|
||||
$size = strlen( (string) maybe_serialize( $value ) );
|
||||
|
||||
$this->data->trans[] = array(
|
||||
'name' => $name,
|
||||
'filtered_trace' => $trace->get_filtered_trace(),
|
||||
'component' => $trace->get_component(),
|
||||
'type' => $type,
|
||||
'value' => $value,
|
||||
'expiration' => $expiration,
|
||||
'exp_diff' => ( $expiration ? human_time_diff( 0, $expiration ) : '' ),
|
||||
'size' => $size,
|
||||
'size_formatted' => (string) size_format( $size ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function process() {
|
||||
$this->data->has_type = is_multisite();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Load early in case a plugin is setting transients when it initialises instead of after the `plugins_loaded` hook
|
||||
QM_Collectors::add( new QM_Collector_Transients() );
|
109
wp-content/plugins/query-monitor/composer.json
Normal file
109
wp-content/plugins/query-monitor/composer.json
Normal file
@ -0,0 +1,109 @@
|
||||
{
|
||||
"name": "johnbillion/query-monitor",
|
||||
"description": "The Developer Tools panel for WordPress.",
|
||||
"license": "GPL-2.0-or-later",
|
||||
"type": "wordpress-plugin",
|
||||
"authors": [
|
||||
{
|
||||
"name": "John Blackbourn",
|
||||
"homepage": "https://johnblackbourn.com/"
|
||||
}
|
||||
],
|
||||
"homepage": "https://github.com/johnbillion/query-monitor/",
|
||||
"support": {
|
||||
"issues": "https://github.com/johnbillion/query-monitor/issues",
|
||||
"forum": "https://wordpress.org/support/plugin/query-monitor",
|
||||
"source": "https://github.com/johnbillion/query-monitor"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/johnbillion"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.4.0",
|
||||
"composer/installers": "^1.0 || ^2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"codeception/module-asserts": "^1.0",
|
||||
"codeception/module-db": "^1.0",
|
||||
"codeception/module-webdriver": "^1.0",
|
||||
"codeception/util-universalframework": "^1.0",
|
||||
"dealerdirect/phpcodesniffer-composer-installer": "0.7.2",
|
||||
"ergebnis/composer-normalize": "^2",
|
||||
"johnbillion/plugin-infrastructure": "dev-trunk",
|
||||
"lucatume/wp-browser": "^3.0.21",
|
||||
"phpcompatibility/phpcompatibility-wp": "2.1.4",
|
||||
"phpstan/phpstan": "^1.0",
|
||||
"phpstan/phpstan-phpunit": "^1.0",
|
||||
"roots/wordpress": "*",
|
||||
"squizlabs/php_codesniffer": "3.7.1",
|
||||
"szepeviktor/phpstan-wordpress": "1.3.0",
|
||||
"wp-coding-standards/wpcs": "2.3.0"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
"classes",
|
||||
"data",
|
||||
"output"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"QM\\Tests\\": "tests/integration"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"dealerdirect/phpcodesniffer-composer-installer": true,
|
||||
"ergebnis/composer-normalize": true,
|
||||
"roots/wordpress-core-installer": true,
|
||||
"composer/installers": true
|
||||
},
|
||||
"classmap-authoritative": true,
|
||||
"preferred-install": "dist",
|
||||
"prepend-autoloader": false,
|
||||
"sort-packages": true
|
||||
},
|
||||
"extra": {
|
||||
"wordpress-install-dir": "vendor/roots/wordpress"
|
||||
},
|
||||
"scripts": {
|
||||
"build-vendor": [
|
||||
"build-vendor"
|
||||
],
|
||||
"test": [
|
||||
"@composer validate --strict --no-check-lock",
|
||||
"@composer normalize --dry-run",
|
||||
"@test:phpstan",
|
||||
"@test:phpcs",
|
||||
"@test:integration",
|
||||
"@test:acceptance"
|
||||
],
|
||||
"test:acceptance": [
|
||||
"npm run build",
|
||||
"acceptance-tests"
|
||||
],
|
||||
"test:destroy": [
|
||||
"COMPOSE_PROJECT_NAME=query-monitor docker-compose down --volumes --remove-orphans"
|
||||
],
|
||||
"test:integration": [
|
||||
"integration-tests"
|
||||
],
|
||||
"test:phpcs": [
|
||||
"phpcs -nps --colors --report-code --report-summary --report-width=80 --cache=tests/cache/phpcs.json --basepath='./' --standard=phpcs53.xml",
|
||||
"phpcs -nps --colors --report-code --report-summary --report-width=80 --cache=tests/cache/phpcs.json --basepath='./' ."
|
||||
],
|
||||
"test:phpstan": [
|
||||
"codecept build",
|
||||
"phpstan analyze --memory-limit=1024M"
|
||||
],
|
||||
"test:start": [
|
||||
"COMPOSE_PROJECT_NAME=query-monitor docker-compose up -d"
|
||||
],
|
||||
"test:stop": [
|
||||
"COMPOSE_PROJECT_NAME=query-monitor docker-compose down"
|
||||
]
|
||||
}
|
||||
}
|
44
wp-content/plugins/query-monitor/data/admin.php
Normal file
44
wp-content/plugins/query-monitor/data/admin.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Admin screen data transfer object.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_Data_Admin extends QM_Data {
|
||||
/**
|
||||
* @var ?WP_Screen
|
||||
*/
|
||||
public $current_screen;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $hook_suffix;
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
* @phpstan-var array{}|array{
|
||||
* columns_filter: string,
|
||||
* sortables_filter: string,
|
||||
* column_action: string,
|
||||
* class_name?: string,
|
||||
* }
|
||||
*/
|
||||
public $list_table;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $pagenow;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $taxnow;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $typenow;
|
||||
}
|
73
wp-content/plugins/query-monitor/data/assets.php
Normal file
73
wp-content/plugins/query-monitor/data/assets.php
Normal file
@ -0,0 +1,73 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Asset data transfer object.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_Data_Assets extends QM_Data {
|
||||
/**
|
||||
* @var array<string, array<string, array<string, mixed>>>
|
||||
*/
|
||||
public $assets;
|
||||
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
public $broken;
|
||||
|
||||
/**
|
||||
* @var array<string, int>
|
||||
*/
|
||||
public $counts;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $default_version;
|
||||
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
public $dependencies;
|
||||
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
public $dependents;
|
||||
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
public $footer;
|
||||
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
public $header;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $host;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $is_ssl;
|
||||
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
public $missing;
|
||||
|
||||
/**
|
||||
* @var array<string, true>
|
||||
*/
|
||||
public $missing_dependencies;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $port;
|
||||
}
|
44
wp-content/plugins/query-monitor/data/block_editor.php
Normal file
44
wp-content/plugins/query-monitor/data/block_editor.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Block editor data transfer object.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_Data_Block_Editor extends QM_Data {
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
public $all_dynamic_blocks;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $block_editor_enabled;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $has_block_context;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $has_block_timing;
|
||||
|
||||
/**
|
||||
* @var array<int, mixed>|null
|
||||
*/
|
||||
public $post_blocks;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $post_has_blocks;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $total_blocks;
|
||||
|
||||
}
|
44
wp-content/plugins/query-monitor/data/cache.php
Normal file
44
wp-content/plugins/query-monitor/data/cache.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Cache data transfer object.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_Data_Cache extends QM_Data {
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $has_object_cache;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $display_hit_rate_warning;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $has_opcode_cache;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $cache_hit_percentage;
|
||||
|
||||
/**
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
public $stats;
|
||||
|
||||
/**
|
||||
* @var array<string, bool>
|
||||
*/
|
||||
public $object_cache_extensions;
|
||||
|
||||
/**
|
||||
* @var array<string, bool>
|
||||
*/
|
||||
public $opcode_cache_extensions;
|
||||
|
||||
}
|
37
wp-content/plugins/query-monitor/data/caps.php
Normal file
37
wp-content/plugins/query-monitor/data/caps.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Cache data transfer object.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_Data_Caps extends QM_Data {
|
||||
/**
|
||||
* @var array<int, array<string, mixed>>
|
||||
* @phpstan-var list<array{
|
||||
* args: list<mixed>,
|
||||
* filtered_trace: list<array<string, mixed>>,
|
||||
* component: QM_Component,
|
||||
* result: bool,
|
||||
* parts: list<string>,
|
||||
* name: string,
|
||||
* user: string,
|
||||
* }>
|
||||
*/
|
||||
public $caps;
|
||||
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
public $parts;
|
||||
|
||||
/**
|
||||
* @var array<int, int>
|
||||
*/
|
||||
public $users;
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
public $components;
|
||||
}
|
18
wp-content/plugins/query-monitor/data/conditionals.php
Normal file
18
wp-content/plugins/query-monitor/data/conditionals.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Conditionals data transfer object.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_Data_Conditionals extends QM_Data {
|
||||
/**
|
||||
* @var array<string, array<int, string>>
|
||||
* @phpstan-var array{
|
||||
* true: list<string>,
|
||||
* false: list<string>,
|
||||
* na: list<string>,
|
||||
* }
|
||||
*/
|
||||
public $conds;
|
||||
}
|
18
wp-content/plugins/query-monitor/data/db_callers.php
Normal file
18
wp-content/plugins/query-monitor/data/db_callers.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Database query callers data transfer object.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_Data_DB_Callers extends QM_Data {
|
||||
/**
|
||||
* @var array<string, array<string, mixed>>
|
||||
* @phpstan-var array<string, array{
|
||||
* caller: string,
|
||||
* ltime: float,
|
||||
* types: array<string, int>,
|
||||
* }>
|
||||
*/
|
||||
public $times = array();
|
||||
}
|
18
wp-content/plugins/query-monitor/data/db_components.php
Normal file
18
wp-content/plugins/query-monitor/data/db_components.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Database query components data transfer object.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_Data_DB_Components extends QM_Data {
|
||||
/**
|
||||
* @var array<string, array<string, mixed>>
|
||||
* @phpstan-var array<string, array{
|
||||
* ltime: float,
|
||||
* types: array<string, int>,
|
||||
* component: string,
|
||||
* }>
|
||||
*/
|
||||
public $times;
|
||||
}
|
38
wp-content/plugins/query-monitor/data/db_dupes.php
Normal file
38
wp-content/plugins/query-monitor/data/db_dupes.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Duplicate database queries data transfer object.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_Data_DB_Dupes extends QM_Data {
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $total_qs;
|
||||
|
||||
/**
|
||||
* @var array<string, array<string, int>>
|
||||
*/
|
||||
public $dupe_sources;
|
||||
|
||||
/**
|
||||
* @var array<string, array<string, int>>
|
||||
*/
|
||||
public $dupe_callers;
|
||||
|
||||
/**
|
||||
* @var array<string, array<string, int>>
|
||||
*/
|
||||
public $dupe_components;
|
||||
|
||||
/**
|
||||
* @var array<string, array<int, int>>
|
||||
*/
|
||||
public $dupes;
|
||||
|
||||
/**
|
||||
* @var array<string, float>
|
||||
*/
|
||||
public $dupe_times;
|
||||
}
|
48
wp-content/plugins/query-monitor/data/db_queries.php
Normal file
48
wp-content/plugins/query-monitor/data/db_queries.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Database queries data transfer object.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_Data_DB_Queries extends QM_Data {
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $total_qs;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
public $total_time;
|
||||
|
||||
/**
|
||||
* @var array<int, array<string, mixed>>
|
||||
*/
|
||||
public $errors;
|
||||
|
||||
/**
|
||||
* @var ?array<int, array<string, mixed>>
|
||||
*/
|
||||
public $expensive;
|
||||
|
||||
/**
|
||||
* @var ?stdClass
|
||||
*/
|
||||
public $wpdb;
|
||||
|
||||
/**
|
||||
* @var ?array<string, array<string, mixed>>
|
||||
* @phpstan-var ?array<string, array{
|
||||
* caller: string,
|
||||
* ltime: float,
|
||||
* types: array<string, int>,
|
||||
* }>
|
||||
*/
|
||||
public $times = array();
|
||||
|
||||
/**
|
||||
* @var ?array<string, array<int, int>>
|
||||
*/
|
||||
public $dupes;
|
||||
}
|
19
wp-content/plugins/query-monitor/data/doing_it_wrong.php
Normal file
19
wp-content/plugins/query-monitor/data/doing_it_wrong.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Doing it Wrong data transfer object.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_Data_Doing_It_Wrong extends QM_Data {
|
||||
/**
|
||||
* @var array<int, array<string, mixed>>
|
||||
* @phpstan-var array<int, array{
|
||||
* hook: string,
|
||||
* filtered_trace: list<array<string, mixed>>,
|
||||
* message: string,
|
||||
* component: QM_Component,
|
||||
* }>
|
||||
*/
|
||||
public $actions;
|
||||
}
|
68
wp-content/plugins/query-monitor/data/environment.php
Normal file
68
wp-content/plugins/query-monitor/data/environment.php
Normal file
@ -0,0 +1,68 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Environment data transfer object.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_Data_Environment extends QM_Data {
|
||||
/**
|
||||
* @TODO data class
|
||||
* @var array<string, mixed>
|
||||
* @phpstan-var array{
|
||||
* variables: array<string, string|null>,
|
||||
* version: string|false,
|
||||
* sapi: string|false,
|
||||
* user: string,
|
||||
* old: bool,
|
||||
* extensions: array<string, string>,
|
||||
* error_reporting: int,
|
||||
* error_levels: array<string, bool>,
|
||||
* }
|
||||
*/
|
||||
public $php;
|
||||
|
||||
/**
|
||||
* @TODO data class
|
||||
* @var array<string, mixed>
|
||||
* @phpstan-var array{
|
||||
* info: array{
|
||||
* server-version: string,
|
||||
* extension: string|null,
|
||||
* client-version: string|null,
|
||||
* user: string,
|
||||
* host: string,
|
||||
* database: string,
|
||||
* },
|
||||
* vars: array<string, bool|string>,
|
||||
* variables: list<stdClass>,
|
||||
* }
|
||||
*/
|
||||
public $db;
|
||||
|
||||
/**
|
||||
* @TODO data class
|
||||
* @var array<string, mixed>
|
||||
* @phpstan-var array{
|
||||
* version: string,
|
||||
* environment_type?: string,
|
||||
* development_mode?: string,
|
||||
* constants: array<string, string>,
|
||||
* }>
|
||||
*/
|
||||
public $wp;
|
||||
|
||||
/**
|
||||
* @TODO data class
|
||||
* @var array<string, mixed>
|
||||
* @phpstan-var array{
|
||||
* name: string,
|
||||
* version: string|null,
|
||||
* address: string|null,
|
||||
* host: string|null,
|
||||
* OS: string|null,
|
||||
* arch: string|null,
|
||||
* }>
|
||||
*/
|
||||
public $server;
|
||||
}
|
10
wp-content/plugins/query-monitor/data/fallback.php
Normal file
10
wp-content/plugins/query-monitor/data/fallback.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Fallback data transfer object for third-party collectors that don't extend
|
||||
* the new QM_DataCollector class.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
#[AllowDynamicProperties]
|
||||
class QM_Data_Fallback extends QM_Data {}
|
37
wp-content/plugins/query-monitor/data/hooks.php
Normal file
37
wp-content/plugins/query-monitor/data/hooks.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Hooks data transfer object.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_Data_Hooks extends QM_Data {
|
||||
/**
|
||||
* @var array<int, array<string, mixed>>
|
||||
* @phpstan-var list<array{
|
||||
* name: string,
|
||||
* actions: list<array{
|
||||
* priority: int,
|
||||
* callback: array<string, mixed>,
|
||||
* }>,
|
||||
* parts: list<string>,
|
||||
* components: array<string, string>,
|
||||
* }>
|
||||
*/
|
||||
public $hooks;
|
||||
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
public $parts;
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
public $components;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $all_hooks;
|
||||
}
|
40
wp-content/plugins/query-monitor/data/http.php
Normal file
40
wp-content/plugins/query-monitor/data/http.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* HTTP data transfer object.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_Data_HTTP extends QM_Data {
|
||||
/**
|
||||
* @var array<string, array<string, mixed>>
|
||||
* @phpstan-var array<string, array{
|
||||
* args: array<string, mixed>,
|
||||
* component: QM_Component,
|
||||
* filtered_trace: list<array<string, mixed>>,
|
||||
* info: array<string, mixed>|null,
|
||||
* host: string,
|
||||
* local: bool,
|
||||
* ltime: float,
|
||||
* redirected_to: string|null,
|
||||
* response: mixed[]|WP_Error,
|
||||
* type: string,
|
||||
* url: string,
|
||||
* }>
|
||||
*/
|
||||
public $http;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
public $ltime;
|
||||
|
||||
/**
|
||||
* @var array<string, array<int, string>>
|
||||
* @phpstan-var array{
|
||||
* alert?: list<string>,
|
||||
* warning?: list<string>,
|
||||
* }
|
||||
*/
|
||||
public $errors;
|
||||
}
|
56
wp-content/plugins/query-monitor/data/languages.php
Normal file
56
wp-content/plugins/query-monitor/data/languages.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Languages data transfer object.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_Data_Languages extends QM_Data {
|
||||
/**
|
||||
* @var array<string, array<string, array<string, mixed>>>
|
||||
* @phpstan-var array<string, array<string, array{
|
||||
* caller: mixed,
|
||||
* domain: string,
|
||||
* file: string|false,
|
||||
* found: int|false,
|
||||
* handle: string|null,
|
||||
* type: 'gettext'|'jed',
|
||||
* }>>
|
||||
*/
|
||||
public $languages;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $locale;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $user_locale;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $determined_locale;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $language_attributes;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $mlp_language;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $pll_language;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $total_size;
|
||||
}
|
30
wp-content/plugins/query-monitor/data/logger.php
Normal file
30
wp-content/plugins/query-monitor/data/logger.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Logger data transfer object.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_Data_Logger extends QM_Data {
|
||||
/**
|
||||
* @var array<string, int>
|
||||
* @phpstan-var array<QM_Collector_Logger::*, int>
|
||||
*/
|
||||
public $counts;
|
||||
|
||||
/**
|
||||
* @var array<int, array<string, mixed>>
|
||||
* @phpstan-var list<array{
|
||||
* message: string,
|
||||
* filtered_trace: mixed[],
|
||||
* component: QM_Component,
|
||||
* level: QM_Collector_Logger::*,
|
||||
* }>
|
||||
*/
|
||||
public $logs;
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
public $components;
|
||||
}
|
19
wp-content/plugins/query-monitor/data/multisite.php
Normal file
19
wp-content/plugins/query-monitor/data/multisite.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Multisite data transfer object.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_Data_Multisite extends QM_Data {
|
||||
/**
|
||||
* @var array<int, array<string, mixed>>
|
||||
* @phpstan-var list<array{
|
||||
* new: int,
|
||||
* prev: int,
|
||||
* to: bool,
|
||||
* trace: QM_Backtrace,
|
||||
* }>
|
||||
*/
|
||||
public $switches;
|
||||
}
|
69
wp-content/plugins/query-monitor/data/overview.php
Normal file
69
wp-content/plugins/query-monitor/data/overview.php
Normal file
@ -0,0 +1,69 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Overview data transfer object.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_Data_Overview extends QM_Data {
|
||||
/**
|
||||
* @var ?float
|
||||
*/
|
||||
public $time_taken;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $time_limit;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
public $time_start;
|
||||
|
||||
/**
|
||||
* @var int|float
|
||||
*/
|
||||
public $time_usage;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $memory;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
public $memory_limit;
|
||||
|
||||
/**
|
||||
* @var int|float
|
||||
*/
|
||||
public $memory_usage;
|
||||
|
||||
/**
|
||||
* @var ?array<string, mixed>
|
||||
*/
|
||||
public $current_user;
|
||||
|
||||
/**
|
||||
* @var ?array<string, mixed>
|
||||
*/
|
||||
public $switched_user;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $display_time_usage_warning;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $display_memory_usage_warning;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $is_admin;
|
||||
|
||||
}
|
45
wp-content/plugins/query-monitor/data/php_errors.php
Normal file
45
wp-content/plugins/query-monitor/data/php_errors.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* PHP errors data transfer object.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
/**
|
||||
* @phpstan-type errorObject array{
|
||||
* errno: int,
|
||||
* type: string,
|
||||
* message: string,
|
||||
* file: string|null,
|
||||
* filename: string,
|
||||
* line: int|null,
|
||||
* filtered_trace: list<array<string, mixed>>|null,
|
||||
* component: QM_Component,
|
||||
* calls: int,
|
||||
* }
|
||||
* @phpstan-type errorObjects array<string, array<string, errorObject>>
|
||||
*/
|
||||
class QM_Data_PHP_Errors extends QM_Data {
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
public $components;
|
||||
|
||||
/**
|
||||
* @var array<string, array<string, array<string, mixed>>>
|
||||
* @phpstan-var errorObjects
|
||||
*/
|
||||
public $errors;
|
||||
|
||||
/**
|
||||
* @var array<string, array<string, array<string, mixed>>>
|
||||
* @phpstan-var errorObjects
|
||||
*/
|
||||
public $suppressed;
|
||||
|
||||
/**
|
||||
* @var array<string, array<string, array<string, mixed>>>
|
||||
* @phpstan-var errorObjects
|
||||
*/
|
||||
public $silenced;
|
||||
}
|
18
wp-content/plugins/query-monitor/data/raw_request.php
Normal file
18
wp-content/plugins/query-monitor/data/raw_request.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Raw request data transfer object.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_Data_Raw_Request extends QM_Data {
|
||||
/**
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
public $request;
|
||||
|
||||
/**
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
public $response;
|
||||
}
|
23
wp-content/plugins/query-monitor/data/redirect.php
Normal file
23
wp-content/plugins/query-monitor/data/redirect.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Redirect data transfer object.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_Data_Redirect extends QM_Data {
|
||||
/**
|
||||
* @var ?QM_Backtrace
|
||||
*/
|
||||
public $trace;
|
||||
|
||||
/**
|
||||
* @var ?string
|
||||
*/
|
||||
public $location;
|
||||
|
||||
/**
|
||||
* @var ?int
|
||||
*/
|
||||
public $status;
|
||||
}
|
52
wp-content/plugins/query-monitor/data/request.php
Normal file
52
wp-content/plugins/query-monitor/data/request.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Request data transfer object.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_Data_Request extends QM_Data {
|
||||
/**
|
||||
* @var array<string, mixed>
|
||||
* @phpstan-var array{
|
||||
* title: string,
|
||||
* data: WP_User|false,
|
||||
* }
|
||||
*/
|
||||
public $user;
|
||||
|
||||
/**
|
||||
* @var array<string, array<string, mixed>>
|
||||
*/
|
||||
public $multisite;
|
||||
|
||||
/**
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
public $request;
|
||||
|
||||
/**
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
public $qvars;
|
||||
|
||||
/**
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
public $plugin_qvars;
|
||||
|
||||
/**
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
public $queried_object;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $request_method;
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
public $matching_rewrites;
|
||||
}
|
99
wp-content/plugins/query-monitor/data/theme.php
Normal file
99
wp-content/plugins/query-monitor/data/theme.php
Normal file
@ -0,0 +1,99 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Theme data transfer object.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_Data_Theme extends QM_Data {
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $is_child_theme;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $stylesheet_theme_json;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $template_theme_json;
|
||||
|
||||
/**
|
||||
* @var WP_Block_Template|null
|
||||
*/
|
||||
public $block_template;
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
public $theme_dirs;
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
public $theme_folders;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $stylesheet;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $template;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $theme_template_file;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $template_path;
|
||||
|
||||
/**
|
||||
* @var ?string
|
||||
*/
|
||||
public $template_file;
|
||||
|
||||
/**
|
||||
* @var ?array<int, string>
|
||||
*/
|
||||
public $template_hierarchy;
|
||||
|
||||
/**
|
||||
* @var ?array<int, string>
|
||||
*/
|
||||
public $timber_files;
|
||||
|
||||
/**
|
||||
* @var ?array<int, string>
|
||||
*/
|
||||
public $body_class;
|
||||
|
||||
/**
|
||||
* @var array<string|int, string>
|
||||
*/
|
||||
public $template_parts;
|
||||
|
||||
/**
|
||||
* @var array<string|int, string>
|
||||
*/
|
||||
public $theme_template_parts;
|
||||
|
||||
/**
|
||||
* @var array<string|int, int>
|
||||
*/
|
||||
public $count_template_parts;
|
||||
|
||||
/**
|
||||
* @var array<int, array<string, mixed>>
|
||||
*/
|
||||
public $unsuccessful_template_parts;
|
||||
|
||||
}
|
18
wp-content/plugins/query-monitor/data/timing.php
Normal file
18
wp-content/plugins/query-monitor/data/timing.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Timing data transfer object.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_Data_Timing extends QM_Data {
|
||||
/**
|
||||
* @var array<int, array<string, mixed>>
|
||||
*/
|
||||
public $warning;
|
||||
|
||||
/**
|
||||
* @var array<int, array<string, mixed>>
|
||||
*/
|
||||
public $timing;
|
||||
}
|
28
wp-content/plugins/query-monitor/data/transients.php
Normal file
28
wp-content/plugins/query-monitor/data/transients.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Transients data transfer object.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_Data_Transients extends QM_Data {
|
||||
/**
|
||||
* @var array<int, array{
|
||||
* name: string,
|
||||
* filtered_trace: mixed[],
|
||||
* component: QM_Component,
|
||||
* type: string,
|
||||
* value: mixed,
|
||||
* expiration: int,
|
||||
* exp_diff: string,
|
||||
* size: int,
|
||||
* size_formatted: string,
|
||||
* }>
|
||||
*/
|
||||
public $trans = array();
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $has_type;
|
||||
}
|
127
wp-content/plugins/query-monitor/dispatchers/AJAX.php
Normal file
127
wp-content/plugins/query-monitor/dispatchers/AJAX.php
Normal file
@ -0,0 +1,127 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Ajax request dispatcher.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class QM_Dispatcher_AJAX extends QM_Dispatcher {
|
||||
|
||||
public $id = 'ajax';
|
||||
|
||||
public function __construct( QM_Plugin $qm ) {
|
||||
parent::__construct( $qm );
|
||||
|
||||
// This dispatcher needs to run on a priority lower than 1 so it can output
|
||||
// its headers before wp_ob_end_flush_all() flushes all the output buffers:
|
||||
// https://github.com/WordPress/wordpress-develop/blob/0a3a3c5119897c6d551a42ae9b5dbfa4f576f2c9/src/wp-includes/default-filters.php#L382
|
||||
add_action( 'shutdown', array( $this, 'dispatch' ), 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
|
||||
if ( ! self::user_can_view() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( QM_Util::is_ajax() ) {
|
||||
// Start an output buffer for Ajax requests so headers can be output at the end:
|
||||
ob_start();
|
||||
}
|
||||
|
||||
parent::init();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function dispatch() {
|
||||
|
||||
if ( ! $this->should_dispatch() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->before_output();
|
||||
|
||||
foreach ( $this->get_outputters( 'headers' ) as $id => $output ) {
|
||||
$output->output();
|
||||
}
|
||||
|
||||
$this->after_output();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function before_output() {
|
||||
foreach ( (array) glob( $this->qm->plugin_path( 'output/headers/*.php' ) ) as $file ) {
|
||||
require_once $file;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function after_output() {
|
||||
|
||||
# flush once, because we're nice
|
||||
if ( ob_get_length() ) {
|
||||
ob_flush();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function is_active() {
|
||||
|
||||
if ( ! QM_Util::is_ajax() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! self::user_can_view() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
# If the headers have already been sent then we can't do anything about it
|
||||
if ( headers_sent() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
# Don't process if the minimum required actions haven't fired:
|
||||
if ( is_admin() ) {
|
||||
if ( ! did_action( 'admin_init' ) ) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if ( ! did_action( 'wp' ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, QM_Dispatcher> $dispatchers
|
||||
* @param QM_Plugin $qm
|
||||
* @return array<string, QM_Dispatcher>
|
||||
*/
|
||||
function register_qm_dispatcher_ajax( array $dispatchers, QM_Plugin $qm ) {
|
||||
$dispatchers['ajax'] = new QM_Dispatcher_AJAX( $qm );
|
||||
return $dispatchers;
|
||||
}
|
||||
|
||||
add_filter( 'qm/dispatchers', 'register_qm_dispatcher_ajax', 10, 2 );
|
941
wp-content/plugins/query-monitor/dispatchers/Html.php
Normal file
941
wp-content/plugins/query-monitor/dispatchers/Html.php
Normal file
@ -0,0 +1,941 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* General HTML request dispatcher.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class QM_Dispatcher_Html extends QM_Dispatcher {
|
||||
|
||||
/**
|
||||
* Outputter instances.
|
||||
*
|
||||
* @var array<string, QM_Output_Html> Array of outputters.
|
||||
*/
|
||||
protected $outputters = array();
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $id = 'html';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $did_footer = false;
|
||||
|
||||
/**
|
||||
* @var array<string, mixed[]>
|
||||
*/
|
||||
protected $admin_bar_menu = array();
|
||||
|
||||
/**
|
||||
* @var array<string, mixed[]>
|
||||
*/
|
||||
protected $panel_menu = array();
|
||||
|
||||
public function __construct( QM_Plugin $qm ) {
|
||||
|
||||
add_action( 'admin_bar_menu', array( $this, 'action_admin_bar_menu' ), 999 );
|
||||
add_action( 'wp_ajax_qm_auth_on', array( $this, 'ajax_on' ) );
|
||||
add_action( 'wp_ajax_qm_auth_off', array( $this, 'ajax_off' ) );
|
||||
add_action( 'wp_ajax_qm_editor_set', array( $this, 'ajax_editor_set' ) );
|
||||
add_action( 'wp_ajax_nopriv_qm_auth_off', array( $this, 'ajax_off' ) );
|
||||
|
||||
// 9 is a magic number, it's the latest we can realistically use due to plugins
|
||||
// which call `fastcgi_finish_request()` in a `shutdown` callback hooked on the
|
||||
// default priority of 10, and QM needs to dispatch its output before those.
|
||||
add_action( 'shutdown', array( $this, 'dispatch' ), 9 );
|
||||
|
||||
add_action( 'wp_footer', array( $this, 'action_footer' ) );
|
||||
add_action( 'admin_footer', array( $this, 'action_footer' ) );
|
||||
add_action( 'login_footer', array( $this, 'action_footer' ) );
|
||||
add_action( 'gp_footer', array( $this, 'action_footer' ) );
|
||||
|
||||
parent::__construct( $qm );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function action_footer() {
|
||||
$this->did_footer = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function. Should the authentication cookie be secure?
|
||||
*
|
||||
* @return bool Should the authentication cookie be secure?
|
||||
*/
|
||||
public static function secure_cookie() {
|
||||
return ( is_ssl() && ( 'https' === parse_url( home_url(), PHP_URL_SCHEME ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function ajax_on() {
|
||||
|
||||
if ( ! current_user_can( 'view_query_monitor' ) || ! check_ajax_referer( 'qm-auth-on', 'nonce', false ) ) {
|
||||
wp_send_json_error();
|
||||
}
|
||||
|
||||
$expiration = time() + ( 2 * DAY_IN_SECONDS );
|
||||
$secure = self::secure_cookie();
|
||||
$cookie = wp_generate_auth_cookie( get_current_user_id(), $expiration, 'logged_in' );
|
||||
$domain = COOKIE_DOMAIN ?: '';
|
||||
|
||||
setcookie( QM_COOKIE, $cookie, $expiration, COOKIEPATH, $domain, $secure, false );
|
||||
|
||||
wp_send_json_success();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function ajax_off() {
|
||||
|
||||
if ( ! self::user_verified() || ! check_ajax_referer( 'qm-auth-off', 'nonce', false ) ) {
|
||||
wp_send_json_error();
|
||||
}
|
||||
|
||||
$expiration = time() - 31536000;
|
||||
$domain = COOKIE_DOMAIN ?: '';
|
||||
|
||||
setcookie( QM_COOKIE, ' ', $expiration, COOKIEPATH, $domain );
|
||||
|
||||
wp_send_json_success();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function ajax_editor_set() {
|
||||
|
||||
if ( ! current_user_can( 'view_query_monitor' ) || ! check_ajax_referer( 'qm-editor-set', 'nonce', false ) ) {
|
||||
wp_send_json_error();
|
||||
}
|
||||
|
||||
$expiration = time() + ( 2 * YEAR_IN_SECONDS );
|
||||
$secure = self::secure_cookie();
|
||||
$editor = wp_unslash( $_POST['editor'] );
|
||||
$domain = COOKIE_DOMAIN ?: '';
|
||||
|
||||
setcookie( QM_EDITOR_COOKIE, $editor, $expiration, COOKIEPATH, $domain, $secure, false );
|
||||
|
||||
wp_send_json_success( $editor );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WP_Admin_Bar $wp_admin_bar
|
||||
* @return void
|
||||
*/
|
||||
public function action_admin_bar_menu( WP_Admin_Bar $wp_admin_bar ) {
|
||||
|
||||
if ( ! self::user_can_view() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$title = __( 'Query Monitor', 'query-monitor' );
|
||||
|
||||
$wp_admin_bar->add_node( array(
|
||||
'id' => 'query-monitor',
|
||||
'title' => esc_html( $title ),
|
||||
'href' => '#qm-overview',
|
||||
) );
|
||||
|
||||
$wp_admin_bar->add_node( array(
|
||||
'parent' => 'query-monitor',
|
||||
'id' => 'query-monitor-placeholder',
|
||||
'title' => esc_html( $title ),
|
||||
'href' => '#qm-overview',
|
||||
) );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
|
||||
if ( ! self::user_can_view() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! self::request_supported() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! file_exists( $this->qm->plugin_path( 'assets/query-monitor.css' ) ) ) {
|
||||
add_action( 'admin_notices', array( $this, 'build_warning' ) );
|
||||
return;
|
||||
}
|
||||
|
||||
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_assets' ), -9999 );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ), -9999 );
|
||||
add_action( 'login_enqueue_scripts', array( $this, 'enqueue_assets' ), -9999 );
|
||||
add_action( 'enqueue_embed_scripts', array( $this, 'enqueue_assets' ), -9999 );
|
||||
|
||||
add_action( 'gp_head', array( $this, 'manually_print_assets' ), 11 );
|
||||
|
||||
parent::init();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function manually_print_assets() {
|
||||
wp_print_scripts( array(
|
||||
'query-monitor',
|
||||
) );
|
||||
wp_print_styles( array(
|
||||
'query-monitor',
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function build_warning() {
|
||||
printf(
|
||||
'<div id="qm-built-nope" class="notice notice-error"><p>%s</p></div>',
|
||||
sprintf(
|
||||
/* translators: 1: CLI command to run, 2: plugin directory name */
|
||||
esc_html__( 'Asset files for Query Monitor need to be built. Run %1$s from the %2$s directory.', 'query-monitor' ),
|
||||
'<code>npm i && npm run build</code>',
|
||||
sprintf(
|
||||
'<code>%s</code>',
|
||||
esc_html( QM_Util::standard_dir( untrailingslashit( $this->qm->plugin_path() ), '' ) )
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue_assets() {
|
||||
global $wp_locale;
|
||||
|
||||
$deps = array(
|
||||
'jquery',
|
||||
);
|
||||
|
||||
if ( defined( 'QM_NO_JQUERY' ) && QM_NO_JQUERY ) {
|
||||
$deps = array();
|
||||
}
|
||||
|
||||
wp_enqueue_style(
|
||||
'query-monitor',
|
||||
$this->qm->plugin_url( 'assets/query-monitor.css' ),
|
||||
array(),
|
||||
QM_VERSION
|
||||
);
|
||||
wp_enqueue_script(
|
||||
'query-monitor',
|
||||
$this->qm->plugin_url( 'assets/query-monitor.js' ),
|
||||
$deps,
|
||||
QM_VERSION,
|
||||
false
|
||||
);
|
||||
wp_localize_script(
|
||||
'query-monitor',
|
||||
'qm_number_format',
|
||||
$wp_locale->number_format
|
||||
);
|
||||
wp_localize_script(
|
||||
'query-monitor',
|
||||
'qm_l10n',
|
||||
array(
|
||||
'ajax_error' => __( 'PHP Errors in Ajax Response', 'query-monitor' ),
|
||||
'ajaxurl' => admin_url( 'admin-ajax.php' ),
|
||||
'auth_nonce' => array(
|
||||
'on' => wp_create_nonce( 'qm-auth-on' ),
|
||||
'off' => wp_create_nonce( 'qm-auth-off' ),
|
||||
'editor-set' => wp_create_nonce( 'qm-editor-set' ),
|
||||
),
|
||||
'fatal_error' => __( 'PHP Fatal Error', 'query-monitor' ),
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* Fires when assets for QM's HTML have been enqueued.
|
||||
*
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @param \QM_Dispatcher_Html $dispatcher The HTML dispatcher.
|
||||
*/
|
||||
do_action( 'qm/output/enqueued-assets', $this );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function dispatch() {
|
||||
if ( ! $this->should_dispatch() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $this->ceased ) {
|
||||
$admin_bar_menu = array(
|
||||
'top' => array(
|
||||
'title' => 'Query Monitor',
|
||||
),
|
||||
'sub' => array(
|
||||
'ceased' => array(
|
||||
'title' => esc_html__( 'Data collection ceased', 'query-monitor' ),
|
||||
'id' => 'query-monitor-ceased',
|
||||
'href' => '#',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$json = array(
|
||||
'menu' => $admin_bar_menu,
|
||||
);
|
||||
|
||||
echo '<!-- Begin Query Monitor output -->' . "\n\n";
|
||||
echo '<script type="text/javascript">' . "\n\n";
|
||||
echo 'var qm = ' . json_encode( $json ) . ';' . "\n\n";
|
||||
echo '</script>' . "\n\n";
|
||||
echo '<div id="query-monitor-ceased"></div>';
|
||||
echo '<!-- End Query Monitor output -->' . "\n\n";
|
||||
return;
|
||||
}
|
||||
|
||||
$switched_locale = self::switch_to_locale( get_user_locale() );
|
||||
|
||||
$this->before_output();
|
||||
|
||||
foreach ( $this->outputters as $id => $output ) {
|
||||
$timer = new QM_Timer();
|
||||
$timer->start();
|
||||
|
||||
printf(
|
||||
"\n" . '<!-- Begin %1$s output -->' . "\n" . '<div class="qm-panel-container" id="qm-%1$s-container">' . "\n",
|
||||
esc_html( $id )
|
||||
);
|
||||
$output->output();
|
||||
printf(
|
||||
"\n" . '</div>' . "\n" . '<!-- End %s output -->' . "\n",
|
||||
esc_html( $id )
|
||||
);
|
||||
|
||||
$output->set_timer( $timer->stop() );
|
||||
}
|
||||
|
||||
$this->after_output();
|
||||
|
||||
if ( $switched_locale ) {
|
||||
self::restore_previous_locale();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function before_output() {
|
||||
foreach ( (array) glob( $this->qm->plugin_path( 'output/html/*.php' ) ) as $file ) {
|
||||
require_once $file;
|
||||
}
|
||||
|
||||
/** @var array<string, QM_Output_Html> $outputters */
|
||||
$outputters = $this->get_outputters( 'html' );
|
||||
|
||||
$this->outputters = $outputters;
|
||||
|
||||
/**
|
||||
* Filters the menu items shown in Query Monitor's admin toolbar menu.
|
||||
*
|
||||
* @since 3.0.0
|
||||
*
|
||||
* @param array<string, mixed[]> $menus Array of menus.
|
||||
*/
|
||||
$this->admin_bar_menu = apply_filters( 'qm/output/menus', array() );
|
||||
|
||||
/**
|
||||
* Filters the menu items shown in the panel navigation menu in Query Monitor's output.
|
||||
*
|
||||
* @since 3.0.0
|
||||
*
|
||||
* @param array<string, mixed[]> $admin_bar_menu Array of menus.
|
||||
*/
|
||||
$this->panel_menu = apply_filters( 'qm/output/panel_menus', $this->admin_bar_menu );
|
||||
|
||||
foreach ( $this->outputters as $output_id => $output ) {
|
||||
$collector = $output->get_collector();
|
||||
|
||||
if ( ( ! empty( $collector->concerned_filters ) || ! empty( $collector->concerned_actions ) ) && isset( $this->panel_menu[ 'qm-' . $output_id ] ) ) {
|
||||
$count = count( $collector->concerned_filters ) + count( $collector->concerned_actions );
|
||||
$this->panel_menu[ 'qm-' . $output_id ]['children'][ 'qm-' . $output_id . '-concerned_hooks' ] = array(
|
||||
'href' => esc_attr( '#' . $collector->id() . '-concerned_hooks' ),
|
||||
'title' => sprintf(
|
||||
/* translators: %s: Number of hooks */
|
||||
__( 'Hooks in Use (%s)', 'query-monitor' ),
|
||||
number_format_i18n( $count )
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$class = array(
|
||||
'qm-no-js',
|
||||
);
|
||||
|
||||
if ( did_action( 'wp_head' ) ) {
|
||||
$class[] = sprintf( 'qm-theme-%s', get_template() );
|
||||
$class[] = sprintf( 'qm-theme-%s', get_stylesheet() );
|
||||
}
|
||||
|
||||
if ( ! is_admin_bar_showing() ) {
|
||||
$class[] = 'qm-peek';
|
||||
}
|
||||
|
||||
$json = array(
|
||||
'menu' => $this->js_admin_bar_menu(),
|
||||
'ajax_errors' => array(), # @TODO move this into the php_errors collector
|
||||
);
|
||||
|
||||
echo '<!-- Begin Query Monitor output -->' . "\n\n";
|
||||
echo '<script type="text/javascript">' . "\n\n";
|
||||
echo 'var qm = ' . json_encode( $json ) . ';' . "\n\n";
|
||||
echo '</script>' . "\n\n";
|
||||
|
||||
echo '<svg id="qm-icon-container">';
|
||||
foreach ( (array) glob( $this->qm->plugin_path( 'assets/icons/*.svg' ) ) as $icon ) {
|
||||
if ( ! $icon ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$icon_name = basename( $icon, '.svg' );
|
||||
$contents = (string) file_get_contents( $icon );
|
||||
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo str_replace(
|
||||
'<path ',
|
||||
sprintf(
|
||||
'<path id="qm-icon-%s" ',
|
||||
$icon_name
|
||||
),
|
||||
$contents
|
||||
);
|
||||
}
|
||||
echo '</svg>';
|
||||
|
||||
echo '<div id="query-monitor-main" data-theme="auto" class="' . implode( ' ', array_map( 'esc_attr', $class ) ) . '" dir="ltr">';
|
||||
echo '<div id="qm-side-resizer" class="qm-resizer"></div>';
|
||||
echo '<div id="qm-title" class="qm-resizer">';
|
||||
echo '<h1 class="qm-title-heading">' . esc_html__( 'Query Monitor', 'query-monitor' ) . '</h1>';
|
||||
echo '<div class="qm-title-heading">';
|
||||
echo '<select>';
|
||||
|
||||
printf(
|
||||
'<option value="%1$s">%2$s</option>',
|
||||
'#qm-overview',
|
||||
esc_html__( 'Overview', 'query-monitor' )
|
||||
);
|
||||
|
||||
foreach ( $this->panel_menu as $menu ) {
|
||||
printf(
|
||||
'<option value="%1$s">%2$s</option>',
|
||||
esc_attr( $menu['href'] ),
|
||||
esc_html( $menu['title'] )
|
||||
);
|
||||
if ( ! empty( $menu['children'] ) ) {
|
||||
foreach ( $menu['children'] as $child ) {
|
||||
printf(
|
||||
'<option value="%1$s">└ %2$s</option>',
|
||||
esc_attr( $child['href'] ),
|
||||
esc_html( $child['title'] )
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printf(
|
||||
'<option value="%1$s">%2$s</option>',
|
||||
'#qm-settings',
|
||||
esc_html__( 'Settings', 'query-monitor' )
|
||||
);
|
||||
|
||||
echo '</select>';
|
||||
|
||||
$settings = QueryMonitor::icon( 'admin-generic' );
|
||||
$toggle = QueryMonitor::icon( 'image-rotate-left' );
|
||||
$close = QueryMonitor::icon( 'no-alt' );
|
||||
|
||||
echo '</div>';
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<button class="qm-title-button qm-button-container-settings" aria-label="' . esc_attr__( 'Settings', 'query-monitor' ) . '">' . $settings . '</button>';
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<button class="qm-title-button qm-button-container-position" aria-label="' . esc_html__( 'Toggle panel position', 'query-monitor' ) . '">' . $toggle . '</button>';
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<button class="qm-title-button qm-button-container-close" aria-label="' . esc_attr__( 'Close Panel', 'query-monitor' ) . '">' . $close . '</button>';
|
||||
echo '</div>'; // #qm-title
|
||||
|
||||
echo '<div id="qm-wrapper">';
|
||||
echo '<nav id="qm-panel-menu" aria-labelledby="qm-panel-menu-caption">';
|
||||
echo '<h2 class="qm-screen-reader-text" id="qm-panel-menu-caption">' . esc_html__( 'Query Monitor Menu', 'query-monitor' ) . '</h2>';
|
||||
echo '<ul role="tablist">';
|
||||
|
||||
printf(
|
||||
'<li role="presentation"><button role="tab" data-qm-href="%1$s">%2$s</button></li>',
|
||||
'#qm-overview',
|
||||
esc_html__( 'Overview', 'query-monitor' )
|
||||
);
|
||||
|
||||
foreach ( $this->panel_menu as $id => $menu ) {
|
||||
$this->do_panel_menu_item( $id, $menu );
|
||||
}
|
||||
|
||||
echo '</ul>';
|
||||
echo '</nav>'; // #qm-panel-menu
|
||||
|
||||
echo '<div id="qm-panels">';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
* @param mixed[] $menu
|
||||
* @return void
|
||||
*/
|
||||
protected function do_panel_menu_item( $id, array $menu ) {
|
||||
printf(
|
||||
'<li role="presentation"><button role="tab" data-qm-href="%1$s">%2$s</button>',
|
||||
esc_attr( $menu['href'] ),
|
||||
esc_html( $menu['title'] )
|
||||
);
|
||||
|
||||
if ( ! empty( $menu['children'] ) ) {
|
||||
echo '<ul role="presentation">';
|
||||
foreach ( $menu['children'] as $child_id => $child ) {
|
||||
$this->do_panel_menu_item( $child_id, $child );
|
||||
}
|
||||
echo '</ul>';
|
||||
}
|
||||
|
||||
echo '</li>';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function after_output() {
|
||||
|
||||
$state = self::user_verified() ? 'on' : 'off';
|
||||
$editor = self::editor_cookie();
|
||||
$text = array(
|
||||
'on' => __( 'Clear authentication cookie', 'query-monitor' ),
|
||||
'off' => __( 'Set authentication cookie', 'query-monitor' ),
|
||||
);
|
||||
|
||||
echo '<div class="qm qm-non-tabular" id="qm-settings" data-qm-state="' . esc_attr( $state ) . '">';
|
||||
echo '<h2 class="qm-screen-reader-text">' . esc_html__( 'Settings', 'query-monitor' ) . '</h2>';
|
||||
|
||||
echo '<div class="qm-grid">';
|
||||
echo '<section>';
|
||||
echo '<h3>' . esc_html__( 'Authentication', 'query-monitor' ) . '</h3>';
|
||||
|
||||
echo '<p>' . esc_html__( 'You can set an authentication cookie which allows you to view Query Monitor output when you’re not logged in, or when you’re logged in as a different user.', 'query-monitor' ) . '</p>';
|
||||
|
||||
echo '<p><button class="qm-auth qm-button" data-qm-text-on="' . esc_attr( $text['on'] ) . '" data-qm-text-off="' . esc_attr( $text['off'] ) . '">' . esc_html( $text[ $state ] ) . '</button></p>';
|
||||
|
||||
$yes = QueryMonitor::icon( 'yes-alt' );
|
||||
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<p data-qm-state-visibility="on">' . $yes . ' ' . esc_html__( 'Authentication cookie is set', 'query-monitor' ) . '</p>';
|
||||
|
||||
echo '</section>';
|
||||
|
||||
echo '<section>';
|
||||
|
||||
echo '<h3>' . esc_html__( 'Editor', 'query-monitor' ) . '</h3>';
|
||||
|
||||
if ( ! has_filter( 'qm/output/file_link_format' ) ) {
|
||||
echo '<p>' . esc_html__( 'You can set your editor here, so that when you click on stack trace links the file opens in your editor.', 'query-monitor' ) . '</p>';
|
||||
|
||||
echo '<p>';
|
||||
echo '<select id="qm-editor-select" name="qm-editor-select" class="qm-filter">';
|
||||
|
||||
$editors = array(
|
||||
'Default/Xdebug' => '',
|
||||
'Atom' => 'atom',
|
||||
'Netbeans' => 'netbeans',
|
||||
'Nova' => 'nova',
|
||||
'PhpStorm' => 'phpstorm',
|
||||
'Sublime Text' => 'sublime',
|
||||
'TextMate' => 'textmate',
|
||||
'Visual Studio Code' => 'vscode',
|
||||
);
|
||||
|
||||
foreach ( $editors as $name => $value ) {
|
||||
echo '<option value="' . esc_attr( $value ) . '" ' . selected( $value, $editor, false ) . '>' . esc_html( $name ) . '</option>';
|
||||
}
|
||||
|
||||
echo '</select>';
|
||||
echo '</p><p>';
|
||||
echo '<button class="qm-editor-button qm-button">' . esc_html__( 'Set editor cookie', 'query-monitor' ) . '</button>';
|
||||
echo '</p>';
|
||||
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<p id="qm-editor-save-status">' . $yes . ' ' . esc_html__( 'Saved! Reload to apply changes.', 'query-monitor' ) . '</p>';
|
||||
} else {
|
||||
printf(
|
||||
/* translators: %s: Name of WordPress filter */
|
||||
esc_html__( 'The file link format for your editor is set by the %s filter.', 'query-monitor' ),
|
||||
'<code>qm/output/file_link_format</code>'
|
||||
);
|
||||
echo '</p>';
|
||||
}
|
||||
|
||||
echo '</section>';
|
||||
|
||||
echo '<section>';
|
||||
echo '<h3>' . esc_html__( 'Appearance', 'query-monitor' ) . '</h3>';
|
||||
|
||||
echo '<p>' . esc_html__( 'Your browser color scheme is respected by default. You can override it here.', 'query-monitor' ) . '</p>';
|
||||
|
||||
echo '<ul>';
|
||||
echo '<li><label><input type="radio" class="qm-theme-toggle qm-radio" name="qm-theme" value="auto" checked/>' . esc_html_x( 'Auto', 'colour scheme', 'query-monitor' ) . '</label></li>';
|
||||
echo '<li><label><input type="radio" class="qm-theme-toggle qm-radio" name="qm-theme" value="light"/>' . esc_html_x( 'Light', 'colour scheme', 'query-monitor' ) . '</label></li>';
|
||||
echo '<li><label><input type="radio" class="qm-theme-toggle qm-radio" name="qm-theme" value="dark"/>' . esc_html_x( 'Dark', 'colour scheme', 'query-monitor' ) . '</label></li>';
|
||||
echo '</ul>';
|
||||
echo '</section>';
|
||||
echo '</div>';
|
||||
|
||||
echo '<div class="qm-boxed">';
|
||||
$constants = array(
|
||||
'QM_DB_EXPENSIVE' => array(
|
||||
'label' => __( 'If an individual database query takes longer than this time to execute, it\'s considered "slow" and triggers a warning.', 'query-monitor' ),
|
||||
'default' => 0.05,
|
||||
),
|
||||
'QM_DISABLED' => array(
|
||||
'label' => __( 'Disable Query Monitor entirely.', 'query-monitor' ),
|
||||
'default' => false,
|
||||
),
|
||||
'QM_DISABLE_ERROR_HANDLER' => array(
|
||||
'label' => __( 'Disable the handling of PHP errors.', 'query-monitor' ),
|
||||
'default' => false,
|
||||
),
|
||||
'QM_ENABLE_CAPS_PANEL' => array(
|
||||
'label' => __( 'Enable the Capability Checks panel.', 'query-monitor' ),
|
||||
'default' => false,
|
||||
),
|
||||
'QM_HIDE_CORE_ACTIONS' => array(
|
||||
'label' => __( 'Hide WordPress core on the Hooks & Actions panel.', 'query-monitor' ),
|
||||
'default' => false,
|
||||
),
|
||||
'QM_HIDE_SELF' => array(
|
||||
'label' => __( 'Hide Query Monitor itself from various panels. Set to false if you want to see how Query Monitor hooks into WordPress.', 'query-monitor' ),
|
||||
'default' => true,
|
||||
),
|
||||
'QM_NO_JQUERY' => array(
|
||||
'label' => __( 'Don\'t specify jQuery as a dependency of Query Monitor. If jQuery isn\'t enqueued then Query Monitor will still operate, but with some reduced functionality.', 'query-monitor' ),
|
||||
'default' => false,
|
||||
),
|
||||
'QM_SHOW_ALL_HOOKS' => array(
|
||||
'label' => __( 'In the Hooks & Actions panel, show every hook that has an action or filter attached (instead of every action hook that fired during the request).', 'query-monitor' ),
|
||||
'default' => false,
|
||||
),
|
||||
'QM_DB_SYMLINK' => array(
|
||||
'label' => __( 'Allow the wp-content/db.php file symlink to be put into place during activation. Set to false to prevent the symlink creation.', 'query-monitor' ),
|
||||
'default' => true,
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Filters which PHP constants for configuring Query Monitor are displayed on its settings panel.
|
||||
*
|
||||
* @since 3.12.0
|
||||
*
|
||||
* @param array $constants The displayed settings constants.
|
||||
* @phpstan-param array<string, array{
|
||||
* label: string,
|
||||
* default: mixed,
|
||||
* }> $constants
|
||||
*/
|
||||
$constants = apply_filters( 'qm/constants', $constants );
|
||||
|
||||
echo '<section>';
|
||||
echo '<h3>' . esc_html__( 'Configuration', 'query-monitor' ) . '</h3>';
|
||||
echo '<p>';
|
||||
printf(
|
||||
/* translators: %s: Name of the config file */
|
||||
esc_html__( 'The following PHP constants can be defined in your %s file in order to control the behavior of Query Monitor:', 'query-monitor' ),
|
||||
'<code>wp-config.php</code>'
|
||||
);
|
||||
echo '</p>';
|
||||
|
||||
echo '<dl>';
|
||||
|
||||
foreach ( $constants as $name => $constant ) {
|
||||
echo '<dt><code>' . esc_html( $name ) . '</code></dt>';
|
||||
echo '<dd>';
|
||||
echo esc_html( $constant['label'] );
|
||||
|
||||
$default_value = $constant['default'];
|
||||
if ( is_bool( $default_value ) ) {
|
||||
$default_value = ( $default_value ? 'true' : 'false' );
|
||||
}
|
||||
|
||||
echo '<br><span class="qm-info">';
|
||||
printf(
|
||||
/* translators: %s: Default value for a PHP constant */
|
||||
esc_html__( 'Default value: %s', 'query-monitor' ),
|
||||
'<code>' . esc_html( (string) $default_value ) . '</code>'
|
||||
);
|
||||
echo '</span>';
|
||||
|
||||
if ( defined( $name ) && ( constant( $name ) !== $constant['default'] ) ) {
|
||||
$current_value = constant( $name );
|
||||
if ( is_bool( $current_value ) ) {
|
||||
$current_value = QM_Collector::format_bool_constant( $name );
|
||||
}
|
||||
|
||||
echo '<br><span class="qm-info">';
|
||||
printf(
|
||||
/* translators: %s: Current value for a PHP constant */
|
||||
esc_html__( 'Current value: %s', 'query-monitor' ),
|
||||
'<code>' . esc_html( $current_value ) . '</code>'
|
||||
);
|
||||
echo '</span>';
|
||||
}
|
||||
echo '</dd>';
|
||||
}
|
||||
|
||||
echo '</dl>';
|
||||
echo '</section>';
|
||||
|
||||
echo '</div>';
|
||||
|
||||
echo '</div>'; // #qm-settings
|
||||
|
||||
/**
|
||||
* Fires after settings but before the panel closing tag.
|
||||
*
|
||||
* @since 3.1.0
|
||||
*
|
||||
* @param QM_Dispatcher_Html $dispatcher The HTML dispatcher instance.
|
||||
* @param array<string, QM_Output_Html> $outputters Array of outputters.
|
||||
*/
|
||||
do_action( 'qm/output/after', $this, $this->outputters );
|
||||
|
||||
echo '</div>'; // #qm-panels
|
||||
echo '</div>'; // #qm-wrapper
|
||||
echo '</div>'; // #query-monitor-main
|
||||
|
||||
echo '<script type="text/javascript">' . "\n\n";
|
||||
?>
|
||||
window.addEventListener('load', function() {
|
||||
var main = document.getElementById( 'query-monitor-main' );
|
||||
var broken = document.getElementById( 'qm-broken' );
|
||||
var menu_item = document.getElementById( 'wp-admin-bar-query-monitor' );
|
||||
var admin_bar = document.getElementById( 'wpadminbar' );
|
||||
|
||||
if ( ( 'undefined' === typeof QM_i18n ) && ( ( 'undefined' === typeof jQuery ) || ! window.jQuery ) ) {
|
||||
/* Fallback for worst case scenario */
|
||||
|
||||
if ( 'undefined' === typeof QM_i18n ) {
|
||||
console.error( 'QM error from page: undefined QM_i18n' );
|
||||
}
|
||||
|
||||
if ( main ) {
|
||||
main.className += ' qm-broken';
|
||||
}
|
||||
|
||||
if ( broken ) {
|
||||
console.error( broken.textContent );
|
||||
}
|
||||
|
||||
if ( 'undefined' === typeof jQuery ) {
|
||||
console.error( 'QM error from page: undefined jQuery' );
|
||||
} else if ( ! window.jQuery ) {
|
||||
console.error( 'QM error from page: no jQuery' );
|
||||
}
|
||||
|
||||
if ( menu_item && main ) {
|
||||
menu_item.addEventListener( 'click', function() {
|
||||
main.className += ' qm-show';
|
||||
} );
|
||||
}
|
||||
} else if ( main && ! admin_bar ) {
|
||||
main.className += ' qm-peek';
|
||||
}
|
||||
} );
|
||||
<?php
|
||||
echo '</script>' . "\n\n";
|
||||
echo '<!-- End Query Monitor output -->' . "\n\n";
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $var
|
||||
* @return int|Exception
|
||||
*/
|
||||
public static function size( $var ) {
|
||||
$start_memory = memory_get_usage();
|
||||
|
||||
try {
|
||||
$var = unserialize( serialize( $var ) ); // phpcs:ignore
|
||||
} catch ( Exception $e ) {
|
||||
return $e;
|
||||
}
|
||||
|
||||
return memory_get_usage() - $start_memory - ( PHP_INT_SIZE * 8 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function js_admin_bar_menu() {
|
||||
|
||||
/**
|
||||
* Filters the CSS class names used on Query Monitor's admin toolbar menu.
|
||||
*
|
||||
* @since 2.7.0
|
||||
*
|
||||
* @param array $menu_classes Array of menu classes.
|
||||
*/
|
||||
$class = implode( ' ', apply_filters( 'qm/output/menu_class', array() ) );
|
||||
|
||||
if ( false === strpos( $class, 'qm-' ) ) {
|
||||
$class .= ' qm-all-clear';
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the title used in Query Monitor's admin toolbar menu.
|
||||
*
|
||||
* @since 2.7.0
|
||||
*
|
||||
* @param array $output_title List of titles.
|
||||
*/
|
||||
$title = implode( ' ', apply_filters( 'qm/output/title', array() ) );
|
||||
|
||||
if ( empty( $title ) ) {
|
||||
$title = esc_html__( 'Query Monitor', 'query-monitor' );
|
||||
}
|
||||
|
||||
$admin_bar_menu = array(
|
||||
'top' => array(
|
||||
'title' => sprintf(
|
||||
'<span class="ab-icon">QM</span><span class="ab-label">%s</span>',
|
||||
$title
|
||||
),
|
||||
'classname' => $class,
|
||||
),
|
||||
'sub' => array(),
|
||||
);
|
||||
|
||||
foreach ( $this->admin_bar_menu as $menu ) {
|
||||
$admin_bar_menu['sub'][ $menu['id'] ] = $menu;
|
||||
}
|
||||
|
||||
return $admin_bar_menu;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function request_supported() {
|
||||
// Don't dispatch if this is an async request:
|
||||
if ( QM_Util::is_async() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't dispatch during a Customizer preview request:
|
||||
if ( function_exists( 'is_customize_preview' ) && is_customize_preview() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't dispatch during an iframed request, eg the plugin info modal, an upgrader action, or the Customizer:
|
||||
if ( defined( 'IFRAME_REQUEST' ) && IFRAME_REQUEST ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't dispatch inside the Site Editor:
|
||||
if ( isset( $_SERVER['SCRIPT_NAME'] ) && '/wp-admin/site-editor.php' === $_SERVER['SCRIPT_NAME'] ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't dispatch on the interim login screen:
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
if ( ! empty( $_GET['interim-login'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function is_active() {
|
||||
|
||||
if ( ! self::user_can_view() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! $this->did_footer ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! self::request_supported() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't dispatch if the minimum required actions haven't fired:
|
||||
if ( is_admin() ) {
|
||||
if ( ! did_action( 'admin_init' ) ) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if ( ! ( did_action( 'wp' ) || did_action( 'login_init' ) || did_action( 'gp_head' ) ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Back-compat filter. Please use `qm/dispatch/html` instead */
|
||||
if ( ! apply_filters( 'qm/process', true, is_admin_bar_showing() ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! file_exists( $this->qm->plugin_path( 'assets/query-monitor.css' ) ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Cease without deactivating the dispatcher.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function cease() {
|
||||
$this->ceased = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, QM_Dispatcher> $dispatchers
|
||||
* @param QM_Plugin $qm
|
||||
* @return array<string, QM_Dispatcher>
|
||||
*/
|
||||
function register_qm_dispatcher_html( array $dispatchers, QM_Plugin $qm ) {
|
||||
$dispatchers['html'] = new QM_Dispatcher_Html( $qm );
|
||||
return $dispatchers;
|
||||
}
|
||||
|
||||
add_filter( 'qm/dispatchers', 'register_qm_dispatcher_html', 10, 2 );
|
93
wp-content/plugins/query-monitor/dispatchers/REST.php
Normal file
93
wp-content/plugins/query-monitor/dispatchers/REST.php
Normal file
@ -0,0 +1,93 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* REST API request dispatcher.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class QM_Dispatcher_REST extends QM_Dispatcher {
|
||||
|
||||
public $id = 'rest';
|
||||
|
||||
public function __construct( QM_Plugin $qm ) {
|
||||
parent::__construct( $qm );
|
||||
|
||||
add_filter( 'rest_post_dispatch', array( $this, 'filter_rest_post_dispatch' ), 1 );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters a REST API response in order to add QM's headers.
|
||||
*
|
||||
* @param WP_HTTP_Response $result Result to send to the client. Usually a WP_REST_Response.
|
||||
* @return WP_HTTP_Response Result to send to the client.
|
||||
*/
|
||||
public function filter_rest_post_dispatch( WP_HTTP_Response $result ) {
|
||||
|
||||
if ( ! $this->should_dispatch() ) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$this->before_output();
|
||||
|
||||
/** @var array<string, QM_Output_Headers> $outputters */
|
||||
$outputters = $this->get_outputters( 'headers' );
|
||||
|
||||
foreach ( $outputters as $output ) {
|
||||
$output->output();
|
||||
}
|
||||
|
||||
$this->after_output();
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function before_output() {
|
||||
foreach ( (array) glob( $this->qm->plugin_path( 'output/headers/*.php' ) ) as $file ) {
|
||||
include_once $file;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function is_active() {
|
||||
|
||||
# If the headers have already been sent then we can't do anything about it
|
||||
if ( headers_sent() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! defined( 'REST_REQUEST' ) || ! REST_REQUEST ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! self::user_can_view() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, QM_Dispatcher> $dispatchers
|
||||
* @param QM_Plugin $qm
|
||||
* @return array<string, QM_Dispatcher>
|
||||
*/
|
||||
function register_qm_dispatcher_rest( array $dispatchers, QM_Plugin $qm ) {
|
||||
$dispatchers['rest'] = new QM_Dispatcher_REST( $qm );
|
||||
return $dispatchers;
|
||||
}
|
||||
|
||||
add_filter( 'qm/dispatchers', 'register_qm_dispatcher_rest', 10, 2 );
|
@ -0,0 +1,80 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* REST API enveloped request dispatcher.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
class QM_Dispatcher_REST_Envelope extends QM_Dispatcher {
|
||||
|
||||
public $id = 'rest_envelope';
|
||||
|
||||
public function __construct( QM_Plugin $qm ) {
|
||||
parent::__construct( $qm );
|
||||
|
||||
add_filter( 'rest_envelope_response', array( $this, 'filter_rest_envelope_response' ), 999, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the enveloped form of a REST API response to add QM's data.
|
||||
*
|
||||
* @param array<string, mixed> $envelope Envelope data.
|
||||
* @param WP_REST_Response $response Original response data.
|
||||
* @return array<string, mixed> Envelope data.
|
||||
*/
|
||||
public function filter_rest_envelope_response( array $envelope, WP_REST_Response $response ) {
|
||||
if ( ! $this->should_dispatch() ) {
|
||||
return $envelope;
|
||||
}
|
||||
|
||||
$data = array();
|
||||
|
||||
$this->before_output();
|
||||
|
||||
/** @var array<string, QM_Output_Raw> $outputters */
|
||||
$outputters = $this->get_outputters( 'raw' );
|
||||
|
||||
foreach ( $outputters as $id => $output ) {
|
||||
$data[ $id ] = $output->get_output();
|
||||
}
|
||||
|
||||
$this->after_output();
|
||||
|
||||
$envelope['qm'] = $data;
|
||||
|
||||
return $envelope;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function before_output() {
|
||||
foreach ( (array) glob( $this->qm->plugin_path( 'output/raw/*.php' ) ) as $file ) {
|
||||
include_once $file;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function is_active() {
|
||||
if ( ! self::user_can_view() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, QM_Dispatcher> $dispatchers
|
||||
* @param QM_Plugin $qm
|
||||
* @return array<string, QM_Dispatcher>
|
||||
*/
|
||||
function register_qm_dispatcher_rest_envelope( array $dispatchers, QM_Plugin $qm ) {
|
||||
$dispatchers['rest_envelope'] = new QM_Dispatcher_REST_Envelope( $qm );
|
||||
return $dispatchers;
|
||||
}
|
||||
|
||||
add_filter( 'qm/dispatchers', 'register_qm_dispatcher_rest_envelope', 10, 2 );
|
101
wp-content/plugins/query-monitor/dispatchers/Redirect.php
Normal file
101
wp-content/plugins/query-monitor/dispatchers/Redirect.php
Normal file
@ -0,0 +1,101 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* HTTP redirect dispatcher.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class QM_Dispatcher_Redirect extends QM_Dispatcher {
|
||||
|
||||
public $id = 'redirect';
|
||||
|
||||
public function __construct( QM_Plugin $qm ) {
|
||||
parent::__construct( $qm );
|
||||
|
||||
add_filter( 'wp_redirect', array( $this, 'filter_wp_redirect' ), 9999, 2 );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters a redirect location in order to output QM's headers.
|
||||
*
|
||||
* @param string $location The path to redirect to.
|
||||
* @param int $status Status code to use.
|
||||
* @return string
|
||||
*/
|
||||
public function filter_wp_redirect( $location, $status ) {
|
||||
|
||||
if ( ! $this->should_dispatch() ) {
|
||||
return $location;
|
||||
}
|
||||
|
||||
$this->before_output();
|
||||
|
||||
/** @var array<string, QM_Output_Headers> $outputters */
|
||||
$outputters = $this->get_outputters( 'headers' );
|
||||
|
||||
foreach ( $outputters as $output ) {
|
||||
$output->output();
|
||||
}
|
||||
|
||||
$this->after_output();
|
||||
|
||||
return $location;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function before_output() {
|
||||
foreach ( (array) glob( $this->qm->plugin_path( 'output/headers/*.php' ) ) as $file ) {
|
||||
require_once $file;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function is_active() {
|
||||
|
||||
if ( ! self::user_can_view() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
# If the headers have already been sent then we can't do anything about it
|
||||
if ( headers_sent() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
# Don't process if the minimum required actions haven't fired:
|
||||
if ( is_admin() ) {
|
||||
if ( ! did_action( 'admin_init' ) ) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if ( ! ( did_action( 'wp' ) || did_action( 'login_init' ) ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, QM_Dispatcher> $dispatchers
|
||||
* @param QM_Plugin $qm
|
||||
* @return array<string, QM_Dispatcher>
|
||||
*/
|
||||
function register_qm_dispatcher_redirect( array $dispatchers, QM_Plugin $qm ) {
|
||||
$dispatchers['redirect'] = new QM_Dispatcher_Redirect( $qm );
|
||||
return $dispatchers;
|
||||
}
|
||||
|
||||
add_filter( 'qm/dispatchers', 'register_qm_dispatcher_redirect', 10, 2 );
|
168
wp-content/plugins/query-monitor/dispatchers/WP_Die.php
Normal file
168
wp-content/plugins/query-monitor/dispatchers/WP_Die.php
Normal file
@ -0,0 +1,168 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Dispatcher for output that gets added to `wp_die()` calls.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class QM_Dispatcher_WP_Die extends QM_Dispatcher {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $id = 'wp_die';
|
||||
|
||||
/**
|
||||
* @var QM_Backtrace|null
|
||||
*/
|
||||
public $trace = null;
|
||||
|
||||
public function __construct( QM_Plugin $qm ) {
|
||||
add_action( 'shutdown', array( $this, 'dispatch' ), 0 );
|
||||
|
||||
add_filter( 'wp_die_handler', array( $this, 'filter_wp_die_handler' ) );
|
||||
|
||||
parent::__construct( $qm );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable $handler
|
||||
* @return callable
|
||||
*/
|
||||
public function filter_wp_die_handler( $handler ) {
|
||||
$this->trace = new QM_Backtrace( array(
|
||||
'ignore_hook' => array(
|
||||
current_filter() => true,
|
||||
),
|
||||
) );
|
||||
|
||||
return $handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function dispatch() {
|
||||
if ( ! $this->should_dispatch() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$switched_locale = self::switch_to_locale( get_user_locale() );
|
||||
$stack = array();
|
||||
$filtered_trace = $this->trace->get_filtered_trace();
|
||||
$component = $this->trace->get_component();
|
||||
|
||||
foreach ( $filtered_trace as $i => $item ) {
|
||||
$stack[] = QM_Output_Html::output_filename( $item['display'], $item['file'], $item['line'] );
|
||||
}
|
||||
|
||||
?>
|
||||
<style>
|
||||
#query-monitor {
|
||||
position: absolute;
|
||||
margin: 4em 0 1em -2em;
|
||||
border: 1px solid #ccd0d4;
|
||||
box-shadow: 0 1px 1px rgb( 0 0 0 / 4% );
|
||||
background: #fff;
|
||||
padding-top: 1em;
|
||||
max-width: 700px;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
#query-monitor h2 {
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
padding: 7px;
|
||||
background: #f3f3f3;
|
||||
margin: 0;
|
||||
border-top: 1px solid #ddd;
|
||||
}
|
||||
|
||||
#query-monitor ol,
|
||||
#query-monitor p {
|
||||
font-size: 12px;
|
||||
padding: 0;
|
||||
margin: 1em 2.5em;
|
||||
}
|
||||
|
||||
#query-monitor ol {
|
||||
padding: 0 0 1em 0;
|
||||
}
|
||||
|
||||
#query-monitor li {
|
||||
margin: 0 0 0.7em;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
#query-monitor .qm-info {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
#query-monitor a.qm-edit-link svg {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
<?php
|
||||
|
||||
echo '<div id="query-monitor">';
|
||||
|
||||
echo '<p>';
|
||||
|
||||
if ( 'unknown' !== $component->type ) {
|
||||
$name = ( 'plugin' === $component->type ) ? $component->context : $component->name;
|
||||
printf(
|
||||
/* translators: %s: Plugin or theme name */
|
||||
esc_html__( 'This message was triggered by %s.', 'query-monitor' ),
|
||||
'<b>' . esc_html( $name ) . '</b>'
|
||||
);
|
||||
}
|
||||
|
||||
echo '</p>';
|
||||
|
||||
echo '<p>' . esc_html__( 'Call stack:', 'query-monitor' ) . '</p>';
|
||||
echo '<ol>';
|
||||
echo '<li>' . implode( '</li><li>', $stack ) . '</li>'; // WPCS: XSS ok.
|
||||
echo '</ol>';
|
||||
|
||||
echo '<h2>' . esc_html__( 'Query Monitor', 'query-monitor' ) . '</h2>';
|
||||
|
||||
echo '</div>';
|
||||
|
||||
if ( $switched_locale ) {
|
||||
self::restore_previous_locale();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function is_active() {
|
||||
if ( ! $this->trace ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! self::user_can_view() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, QM_Dispatcher> $dispatchers
|
||||
* @param QM_Plugin $qm
|
||||
* @return array<string, QM_Dispatcher>
|
||||
*/
|
||||
function register_qm_dispatcher_wp_die( array $dispatchers, QM_Plugin $qm ) {
|
||||
$dispatchers['wp_die'] = new QM_Dispatcher_WP_Die( $qm );
|
||||
return $dispatchers;
|
||||
}
|
||||
|
||||
add_filter( 'qm/dispatchers', 'register_qm_dispatcher_wp_die', 10, 2 );
|
32
wp-content/plugins/query-monitor/output/Headers.php
Normal file
32
wp-content/plugins/query-monitor/output/Headers.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Abstract output class for HTTP headers.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
abstract class QM_Output_Headers extends QM_Output {
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function output() {
|
||||
|
||||
$id = $this->collector->id;
|
||||
|
||||
foreach ( $this->get_output() as $key => $value ) {
|
||||
if ( ! is_scalar( $value ) ) {
|
||||
$value = json_encode( $value );
|
||||
}
|
||||
|
||||
# Remove illegal characters (Header may not contain NUL bytes)
|
||||
if ( is_string( $value ) ) {
|
||||
$value = str_replace( chr( 0 ), '', $value );
|
||||
}
|
||||
|
||||
header( sprintf( 'X-QM-%s-%s: %s', $id, $key, $value ) );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
626
wp-content/plugins/query-monitor/output/Html.php
Normal file
626
wp-content/plugins/query-monitor/output/Html.php
Normal file
@ -0,0 +1,626 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Abstract output class for HTML pages.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
abstract class QM_Output_Html extends QM_Output {
|
||||
|
||||
/**
|
||||
* @var string|false|null
|
||||
*/
|
||||
protected static $file_link_format = null;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected $current_id = null;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected $current_name = null;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function name() {
|
||||
return $this->collector->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed[]> $menu
|
||||
* @return array<string, mixed[]>
|
||||
*/
|
||||
public function admin_menu( array $menu ) {
|
||||
|
||||
$menu[ $this->collector->id() ] = $this->menu( array(
|
||||
'title' => esc_html( $this->name() ),
|
||||
) );
|
||||
return $menu;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function get_output() {
|
||||
ob_start();
|
||||
// compat until I convert all the existing outputters to use `get_output()`
|
||||
$this->output();
|
||||
$out = (string) ob_get_clean();
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
* @param string $name
|
||||
* @return void
|
||||
*/
|
||||
protected function before_tabular_output( $id = null, $name = null ) {
|
||||
if ( null === $id ) {
|
||||
$id = $this->collector->id();
|
||||
}
|
||||
if ( null === $name ) {
|
||||
$name = $this->name();
|
||||
}
|
||||
|
||||
$this->current_id = $id;
|
||||
$this->current_name = $name;
|
||||
|
||||
printf(
|
||||
'<div class="qm" id="%1$s" role="tabpanel" aria-labelledby="%1$s-caption" tabindex="-1">',
|
||||
esc_attr( $id )
|
||||
);
|
||||
|
||||
echo '<table class="qm-sortable">';
|
||||
|
||||
printf(
|
||||
'<caption class="qm-screen-reader-text"><h2 id="%1$s-caption">%2$s</h2></caption>',
|
||||
esc_attr( $id ),
|
||||
esc_html( $name )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function after_tabular_output() {
|
||||
echo '</table>';
|
||||
echo '</div>';
|
||||
|
||||
$this->output_concerns();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
* @param string $name
|
||||
* @return void
|
||||
*/
|
||||
protected function before_non_tabular_output( $id = null, $name = null ) {
|
||||
if ( null === $id ) {
|
||||
$id = $this->collector->id();
|
||||
}
|
||||
if ( null === $name ) {
|
||||
$name = $this->name();
|
||||
}
|
||||
|
||||
$this->current_id = $id;
|
||||
$this->current_name = $name;
|
||||
|
||||
printf(
|
||||
'<div class="qm qm-non-tabular" id="%1$s" role="tabpanel" aria-labelledby="%1$s-caption" tabindex="-1">',
|
||||
esc_attr( $id )
|
||||
);
|
||||
|
||||
echo '<div class="qm-boxed">';
|
||||
|
||||
printf(
|
||||
'<h2 class="qm-screen-reader-text" id="%1$s-caption">%2$s</h2>',
|
||||
esc_attr( $id ),
|
||||
esc_html( $name )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function after_non_tabular_output() {
|
||||
echo '</div>';
|
||||
echo '</div>';
|
||||
|
||||
$this->output_concerns();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function output_concerns() {
|
||||
$concerns = array(
|
||||
'concerned_actions' => array(
|
||||
__( 'Related Hooks with Actions Attached', 'query-monitor' ),
|
||||
__( 'Action', 'query-monitor' ),
|
||||
),
|
||||
'concerned_filters' => array(
|
||||
__( 'Related Hooks with Filters Attached', 'query-monitor' ),
|
||||
__( 'Filter', 'query-monitor' ),
|
||||
),
|
||||
);
|
||||
|
||||
if ( empty( $this->collector->concerned_actions ) && empty( $this->collector->concerned_filters ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
printf(
|
||||
'<div class="qm qm-concerns" id="%1$s" role="tabpanel" aria-labelledby="%1$s-caption" tabindex="-1">',
|
||||
esc_attr( $this->current_id . '-concerned_hooks' )
|
||||
);
|
||||
|
||||
echo '<table>';
|
||||
|
||||
printf(
|
||||
'<caption><h2 id="%1$s-caption">%2$s</h2></caption>',
|
||||
esc_attr( $this->current_id . '-concerned_hooks' ),
|
||||
sprintf(
|
||||
/* translators: %s: Panel name */
|
||||
esc_html__( '%s: Related Hooks with Filters or Actions Attached', 'query-monitor' ),
|
||||
esc_html( $this->name() )
|
||||
)
|
||||
);
|
||||
|
||||
echo '<thead>';
|
||||
echo '<tr>';
|
||||
echo '<th scope="col">' . esc_html__( 'Hook', 'query-monitor' ) . '</th>';
|
||||
echo '<th scope="col">' . esc_html__( 'Type', 'query-monitor' ) . '</th>';
|
||||
echo '<th scope="col">' . esc_html__( 'Priority', 'query-monitor' ) . '</th>';
|
||||
echo '<th scope="col">' . esc_html__( 'Callback', 'query-monitor' ) . '</th>';
|
||||
echo '<th scope="col">' . esc_html__( 'Component', 'query-monitor' ) . '</th>';
|
||||
echo '</tr>';
|
||||
echo '</thead>';
|
||||
|
||||
echo '<tbody>';
|
||||
|
||||
foreach ( $concerns as $key => $labels ) {
|
||||
if ( empty( $this->collector->$key ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QM_Output_Html_Hooks::output_hook_table( $this->collector->$key, true );
|
||||
}
|
||||
|
||||
echo '</tbody>';
|
||||
echo '</table>';
|
||||
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
* @param string $name
|
||||
* @return void
|
||||
*/
|
||||
protected function before_debug_bar_output( $id = null, $name = null ) {
|
||||
if ( null === $id ) {
|
||||
$id = $this->collector->id();
|
||||
}
|
||||
if ( null === $name ) {
|
||||
$name = $this->name();
|
||||
}
|
||||
|
||||
printf(
|
||||
'<div class="qm qm-debug-bar" id="%1$s" role="tabpanel" aria-labelledby="%1$s-caption" tabindex="-1">',
|
||||
esc_attr( $id )
|
||||
);
|
||||
|
||||
printf(
|
||||
'<h2 class="qm-screen-reader-text" id="%1$s-caption">%2$s</h2>',
|
||||
esc_attr( $id ),
|
||||
esc_html( $name )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function after_debug_bar_output() {
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $notice
|
||||
* @return string
|
||||
*/
|
||||
protected function build_notice( $notice ) {
|
||||
$return = '<section>';
|
||||
$return .= '<div class="qm-notice">';
|
||||
$return .= '<p>';
|
||||
$return .= $notice;
|
||||
$return .= '</p>';
|
||||
$return .= '</div>';
|
||||
$return .= '</section>';
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $vars
|
||||
* @return void
|
||||
*/
|
||||
public static function output_inner( array $vars ) {
|
||||
|
||||
echo '<table>';
|
||||
|
||||
foreach ( $vars as $key => $value ) {
|
||||
echo '<tr>';
|
||||
echo '<td>' . esc_html( $key ) . '</td>';
|
||||
if ( is_array( $value ) ) {
|
||||
echo '<td>';
|
||||
self::output_inner( $value );
|
||||
echo '</td>';
|
||||
} elseif ( is_object( $value ) ) {
|
||||
echo '<td>';
|
||||
self::output_inner( get_object_vars( $value ) );
|
||||
echo '</td>';
|
||||
} elseif ( is_bool( $value ) ) {
|
||||
if ( $value ) {
|
||||
echo '<td class="qm-true">true</td>';
|
||||
} else {
|
||||
echo '<td class="qm-false">false</td>';
|
||||
}
|
||||
} else {
|
||||
echo '<td>';
|
||||
echo nl2br( esc_html( $value ) );
|
||||
echo '</td>';
|
||||
}
|
||||
echo '</td>';
|
||||
echo '</tr>';
|
||||
}
|
||||
echo '</table>';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the table filter controls. Safe for output.
|
||||
*
|
||||
* @param string $name The name for the `data-` attributes that get filtered by this control.
|
||||
* @param (string|int)[] $values Option values for this control.
|
||||
* @param string $label Label text for the filter control.
|
||||
* @param array $args {
|
||||
* @type string $highlight The name for the `data-` attributes that get highlighted by this control.
|
||||
* @type string[] $prepend Associative array of options to prepend to the list of values.
|
||||
* @type string[] $append Associative array of options to append to the list of values.
|
||||
* }
|
||||
* @phpstan-param array{
|
||||
* highlight?: string,
|
||||
* prepend?: array<string, string>,
|
||||
* append?: array<string, string>,
|
||||
* } $args
|
||||
* @return string Markup for the table filter controls.
|
||||
*/
|
||||
protected function build_filter( $name, $values, $label, $args = array() ) {
|
||||
|
||||
if ( empty( $values ) || ! is_array( $values ) ) {
|
||||
return esc_html( $label ); // Return label text, without being marked up as a label element.
|
||||
}
|
||||
|
||||
if ( ! is_array( $args ) ) {
|
||||
$args = array(
|
||||
'highlight' => $args,
|
||||
);
|
||||
}
|
||||
|
||||
$args = array_merge( array(
|
||||
'highlight' => '',
|
||||
'prepend' => array(),
|
||||
'append' => array(),
|
||||
'all' => _x( 'All', '"All" option for filters', 'query-monitor' ),
|
||||
), $args );
|
||||
|
||||
$core_val = __( 'WordPress Core', 'query-monitor' );
|
||||
$core_key = array_search( $core_val, $values, true );
|
||||
|
||||
if ( 'component' === $name && count( $values ) > 1 && false !== $core_key ) {
|
||||
$args['append'][ $core_val ] = $core_val;
|
||||
$args['append']['non-core'] = __( 'Non-WordPress Core', 'query-monitor' );
|
||||
unset( $values[ $core_key ] );
|
||||
}
|
||||
|
||||
$filter_id = 'qm-filter-' . $this->collector->id . '-' . $name;
|
||||
|
||||
$out = '<div class="qm-filter-container">';
|
||||
$out .= '<label for="' . esc_attr( $filter_id ) . '">' . esc_html( $label ) . '</label>';
|
||||
$out .= '<select id="' . esc_attr( $filter_id ) . '" class="qm-filter" data-filter="' . esc_attr( $name ) . '" data-highlight="' . esc_attr( $args['highlight'] ) . '">';
|
||||
$out .= '<option value="">' . esc_html( $args['all'] ) . '</option>';
|
||||
|
||||
if ( ! empty( $args['prepend'] ) ) {
|
||||
foreach ( $args['prepend'] as $value => $label ) {
|
||||
$out .= '<option value="' . esc_attr( $value ) . '">' . esc_html( $label ) . '</option>';
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $values as $key => $value ) {
|
||||
if ( is_int( $key ) && $key >= 0 ) {
|
||||
$out .= '<option value="' . esc_attr( $value ) . '">' . esc_html( $value ) . '</option>';
|
||||
} else {
|
||||
$out .= '<option value="' . esc_attr( $key ) . '">' . esc_html( $value ) . '</option>';
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $args['append'] ) ) {
|
||||
foreach ( $args['append'] as $value => $label ) {
|
||||
$out .= '<option value="' . esc_attr( $value ) . '">' . esc_html( $label ) . '</option>';
|
||||
}
|
||||
}
|
||||
|
||||
$out .= '</select>';
|
||||
$out .= '</div>';
|
||||
|
||||
return $out;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the column sorter controls. Safe for output.
|
||||
*
|
||||
* @param string $heading Heading text for the column. Optional.
|
||||
* @return string Markup for the column sorter controls.
|
||||
*/
|
||||
protected function build_sorter( $heading = '' ) {
|
||||
$out = '';
|
||||
$out .= '<span class="qm-th">';
|
||||
$out .= '<span class="qm-sort-heading">';
|
||||
|
||||
if ( '#' === $heading ) {
|
||||
$out .= '<span class="qm-screen-reader-text">' . esc_html__( 'Sequence', 'query-monitor' ) . '</span>';
|
||||
} elseif ( $heading ) {
|
||||
$out .= esc_html( $heading );
|
||||
}
|
||||
|
||||
$out .= '</span>';
|
||||
$out .= '<button class="qm-sort-controls" aria-label="' . esc_attr__( 'Sort data by this column', 'query-monitor' ) . '">';
|
||||
$out .= QueryMonitor::icon( 'arrow-down' );
|
||||
$out .= '</button>';
|
||||
$out .= '</span>';
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a toggle control. Safe for output.
|
||||
*
|
||||
* @return string Markup for the column sorter controls.
|
||||
*/
|
||||
protected static function build_toggler() {
|
||||
$out = '<button class="qm-toggle" data-on="+" data-off="-" aria-expanded="false" aria-label="' . esc_attr__( 'Toggle more information', 'query-monitor' ) . '"><span aria-hidden="true">+</span></button>';
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a filter trigger.
|
||||
*
|
||||
* @param string $target
|
||||
* @param string $filter
|
||||
* @param string $value
|
||||
* @param string $label
|
||||
* @return string
|
||||
*/
|
||||
protected static function build_filter_trigger( $target, $filter, $value, $label ) {
|
||||
return sprintf(
|
||||
'<button class="qm-filter-trigger" data-qm-target="%1$s" data-qm-filter="%2$s" data-qm-value="%3$s">%4$s%5$s</button>',
|
||||
esc_attr( $target ),
|
||||
esc_attr( $filter ),
|
||||
esc_attr( $value ),
|
||||
$label,
|
||||
QueryMonitor::icon( 'filter' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a link.
|
||||
*
|
||||
* @param string $href
|
||||
* @param string $label
|
||||
* @return string
|
||||
*/
|
||||
protected static function build_link( $href, $label ) {
|
||||
return sprintf(
|
||||
'<a href="%1$s" class="qm-link">%2$s%3$s</a>',
|
||||
esc_attr( $href ),
|
||||
$label,
|
||||
QueryMonitor::icon( 'external' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $args
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
protected function menu( array $args ) {
|
||||
|
||||
return array_merge( array(
|
||||
'id' => esc_attr( "query-monitor-{$this->collector->id}" ),
|
||||
'href' => esc_attr( '#' . $this->collector->id() ),
|
||||
), $args );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the given SQL string in a nicely presented format. Safe for output.
|
||||
*
|
||||
* @param string $sql An SQL query string.
|
||||
* @return string The SQL formatted with markup.
|
||||
*/
|
||||
public static function format_sql( $sql ) {
|
||||
|
||||
$sql = str_replace( array( "\r\n", "\r", "\n", "\t" ), ' ', $sql );
|
||||
$sql = esc_html( $sql );
|
||||
$sql = trim( $sql );
|
||||
|
||||
$regex = 'ADD|AFTER|ALTER|AND|BEGIN|COMMIT|CREATE|DELETE|DESCRIBE|DO|DROP|ELSE|END|EXCEPT|EXPLAIN|FROM|GROUP|HAVING|INNER|INSERT|INTERSECT|LEFT|LIMIT|ON|OR|ORDER|OUTER|RENAME|REPLACE|RIGHT|ROLLBACK|SELECT|SET|SHOW|START|THEN|TRUNCATE|UNION|UPDATE|USE|USING|VALUES|WHEN|WHERE|XOR';
|
||||
$sql = preg_replace( '# (' . $regex . ') #', '<br> $1 ', $sql );
|
||||
|
||||
$keywords = '\b(?:ACTION|ADD|AFTER|AGAINST|ALTER|AND|ASC|AS|AUTO_INCREMENT|BEGIN|BETWEEN|BIGINT|BINARY|BIT|BLOB|BOOLEAN|BOOL|BREAK|BY|CASE|COLLATE|COLUMNS?|COMMIT|CONTINUE|CREATE|DATA(?:BASES?)?|DATE(?:TIME)?|DECIMAL|DECLARE|DEC|DEFAULT|DELAYED|DELETE|DESCRIBE|DESC|DISTINCT|DOUBLE|DO|DROP|DUPLICATE|ELSE|END|ENUM|EXCEPT|EXISTS|EXPLAIN|FIELDS|FLOAT|FORCE|FOREIGN|FOR|FROM|FULL|FUNCTION|GROUP|HAVING|IF|IGNORE|INDEX|INNER|INSERT|INTEGER|INTERSECT|INTERVAL|INTO|INT|IN|IS|JOIN|KEYS?|LEFT|LIKE|LIMIT|LONG(?:BLOB|TEXT)|MEDIUM(?:BLOB|INT|TEXT)|MATCH|MERGE|MIDDLEINT|NOT|NO|NULLIF|ON|ORDER|OR|OUTER|PRIMARY|PROC(?:EDURE)?|REGEXP|RENAME|REPLACE|RIGHT|RLIKE|ROLLBACK|SCHEMA|SELECT|SET|SHOW|SMALLINT|START|TABLES?|TEXT(?:SIZE)?|THEN|TIME(?:STAMP)?|TINY(?:BLOB|INT|TEXT)|TRUNCATE|UNION|UNIQUE|UNSIGNED|UPDATE|USE|USING|VALUES?|VAR(?:BINARY|CHAR)|WHEN|WHERE|WHILE|XOR)\b';
|
||||
$sql = preg_replace( '#' . $keywords . '#', '<b>$0</b>', $sql );
|
||||
|
||||
return '<code>' . $sql . '</code>';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the given URL in a nicely presented format. Safe for output.
|
||||
*
|
||||
* @param string $url A URL.
|
||||
* @return string The URL formatted with markup.
|
||||
*/
|
||||
public static function format_url( $url ) {
|
||||
return str_replace( array( '?', '&' ), array( '<br>?', '<br>&' ), esc_html( $url ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a file path, name, and line number, or a clickable link to the file. Safe for output.
|
||||
*
|
||||
* @link https://querymonitor.com/blog/2019/02/clickable-stack-traces-and-function-names-in-query-monitor/
|
||||
*
|
||||
* @param string $text The display text, such as a function name or file name.
|
||||
* @param string $file The full file path and name.
|
||||
* @param int $line Optional. A line number, if appropriate.
|
||||
* @param bool $is_filename Optional. Is the text a plain file name? Default false.
|
||||
* @return string The fully formatted file link or file name, safe for output.
|
||||
*/
|
||||
public static function output_filename( $text, $file, $line = 0, $is_filename = false ) {
|
||||
if ( empty( $file ) ) {
|
||||
if ( $is_filename ) {
|
||||
return esc_html( $text );
|
||||
} else {
|
||||
return '<code>' . esc_html( $text ) . '</code>';
|
||||
}
|
||||
}
|
||||
|
||||
$link_line = $line ?: 1;
|
||||
|
||||
if ( ! self::has_clickable_links() ) {
|
||||
$fallback = QM_Util::standard_dir( $file, '' );
|
||||
if ( $line ) {
|
||||
$fallback .= ':' . $line;
|
||||
}
|
||||
if ( $is_filename ) {
|
||||
$return = esc_html( $text );
|
||||
} else {
|
||||
$return = '<code>' . esc_html( $text ) . '</code>';
|
||||
}
|
||||
if ( $fallback !== $text ) {
|
||||
$return .= '<br><span class="qm-info qm-supplemental">' . esc_html( $fallback ) . '</span>';
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
$map = self::get_file_path_map();
|
||||
|
||||
if ( ! empty( $map ) ) {
|
||||
foreach ( $map as $from => $to ) {
|
||||
$file = str_replace( $from, $to, $file );
|
||||
}
|
||||
}
|
||||
|
||||
/** @var string */
|
||||
$link_format = self::get_file_link_format();
|
||||
$link = sprintf( $link_format, rawurlencode( $file ), intval( $link_line ) );
|
||||
|
||||
if ( $is_filename ) {
|
||||
$format = '<a href="%1$s" class="qm-edit-link">%2$s%3$s</a>';
|
||||
} else {
|
||||
$format = '<a href="%1$s" class="qm-edit-link"><code>%2$s</code>%3$s</a>';
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
$format,
|
||||
esc_attr( $link ),
|
||||
esc_html( $text ),
|
||||
QueryMonitor::icon( 'edit' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a protocol URL for edit links in QM stack traces for various editors.
|
||||
*
|
||||
* @param string $editor The chosen code editor.
|
||||
* @param string|false $default_format A format to use if no editor is found.
|
||||
* @return string|false A protocol URL format or boolean false.
|
||||
*/
|
||||
public static function get_editor_file_link_format( $editor, $default_format ) {
|
||||
switch ( $editor ) {
|
||||
case 'phpstorm':
|
||||
return 'phpstorm://open?file=%f&line=%l';
|
||||
case 'vscode':
|
||||
return 'vscode://file/%f:%l';
|
||||
case 'atom':
|
||||
return 'atom://open/?url=file://%f&line=%l';
|
||||
case 'sublime':
|
||||
return 'subl://open/?url=file://%f&line=%l';
|
||||
case 'textmate':
|
||||
return 'txmt://open/?url=file://%f&line=%l';
|
||||
case 'netbeans':
|
||||
return 'nbopen://%f:%l';
|
||||
case 'nova':
|
||||
return 'nova://open?path=%f&line=%l';
|
||||
default:
|
||||
return $default_format;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|false
|
||||
*/
|
||||
public static function get_file_link_format() {
|
||||
if ( ! isset( self::$file_link_format ) ) {
|
||||
$format = ini_get( 'xdebug.file_link_format' );
|
||||
|
||||
if ( defined( 'QM_EDITOR_COOKIE' ) && isset( $_COOKIE[ QM_EDITOR_COOKIE ] ) ) {
|
||||
$format = self::get_editor_file_link_format(
|
||||
$_COOKIE[ QM_EDITOR_COOKIE ],
|
||||
$format
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the clickable file link format.
|
||||
*
|
||||
* @link https://querymonitor.com/blog/2019/02/clickable-stack-traces-and-function-names-in-query-monitor/
|
||||
* @since 3.0.0
|
||||
*
|
||||
* @param string|false $format The format of the clickable file link, or false if there is none.
|
||||
*/
|
||||
$format = apply_filters( 'qm/output/file_link_format', $format );
|
||||
if ( empty( $format ) ) {
|
||||
self::$file_link_format = false;
|
||||
} else {
|
||||
self::$file_link_format = str_replace( array( '%f', '%l' ), array( '%1$s', '%2$d' ), $format );
|
||||
}
|
||||
}
|
||||
|
||||
return self::$file_link_format;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public static function get_file_path_map() {
|
||||
/**
|
||||
* Filters the file path mapping for clickable file links.
|
||||
*
|
||||
* @link https://querymonitor.com/blog/2019/02/clickable-stack-traces-and-function-names-in-query-monitor/
|
||||
* @since 3.0.0
|
||||
*
|
||||
* @param array<string, string> $file_map Array of file path mappings. Keys are the source paths and values are the replacement paths.
|
||||
*/
|
||||
return apply_filters( 'qm/output/file_path_map', array() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function has_clickable_links() {
|
||||
return ( false !== self::get_file_link_format() );
|
||||
}
|
||||
|
||||
}
|
9
wp-content/plugins/query-monitor/output/Raw.php
Normal file
9
wp-content/plugins/query-monitor/output/Raw.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* Abstract output class for raw output encoded as JSON.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
abstract class QM_Output_Raw extends QM_Output {
|
||||
}
|
73
wp-content/plugins/query-monitor/output/headers/overview.php
Normal file
73
wp-content/plugins/query-monitor/output/headers/overview.php
Normal file
@ -0,0 +1,73 @@
|
||||
<?php declare(strict_types = 1);
|
||||
/**
|
||||
* General overview output for HTTP headers.
|
||||
*
|
||||
* @package query-monitor
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class QM_Output_Headers_Overview extends QM_Output_Headers {
|
||||
|
||||
/**
|
||||
* Collector instance.
|
||||
*
|
||||
* @var QM_Collector_Overview Collector.
|
||||
*/
|
||||
protected $collector;
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function get_output() {
|
||||
/** @var QM_Data_Overview $data */
|
||||
$data = $this->collector->get_data();
|
||||
$headers = array();
|
||||
|
||||
$headers['time_taken'] = number_format_i18n( $data->time_taken, 4 );
|
||||
$headers['time_usage'] = sprintf(
|
||||
/* translators: 1: Percentage of time limit used, 2: Time limit in seconds */
|
||||
__( '%1$s%% of %2$ss limit', 'query-monitor' ),
|
||||
number_format_i18n( $data->time_usage, 1 ),
|
||||
number_format_i18n( $data->time_limit )
|
||||
);
|
||||
|
||||
if ( ! empty( $data->memory ) ) {
|
||||
$headers['memory'] = sprintf(
|
||||
/* translators: %s: Memory used in megabytes */
|
||||
__( '%s MB', 'query-monitor' ),
|
||||
number_format_i18n( ( $data->memory / 1024 / 1024 ), 1 )
|
||||
);
|
||||
|
||||
if ( $data->memory_limit > 0 ) {
|
||||
$headers['memory_usage'] = sprintf(
|
||||
/* translators: 1: Percentage of memory limit used, 2: Memory limit in megabytes */
|
||||
__( '%1$s%% of %2$s MB server limit', 'query-monitor' ),
|
||||
number_format_i18n( $data->memory_usage, 1 ),
|
||||
number_format_i18n( $data->memory_limit / 1024 / 1024 )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $headers;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, QM_Output> $output
|
||||
* @param QM_Collectors $collectors
|
||||
* @return array<string, QM_Output>
|
||||
*/
|
||||
function register_qm_output_headers_overview( array $output, QM_Collectors $collectors ) {
|
||||
$collector = QM_Collectors::get( 'overview' );
|
||||
if ( $collector ) {
|
||||
$output['overview'] = new QM_Output_Headers_Overview( $collector );
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
add_filter( 'qm/outputter/headers', 'register_qm_output_headers_overview', 10, 2 );
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user