Skip to content

Commit 7c72b49

Browse files
CopilotswissspidyCopilot
authored
Introduce wp profile queries command (#207)
Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Pascal Birchler <pascalb@google.com>
1 parent 47fc059 commit 7c72b49

11 files changed

Lines changed: 525 additions & 27 deletions

README.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,75 @@ current theme.
298298
| 0.1009s | 100% | 1 |
299299
+---------+-------------+---------------+
300300

301+
302+
303+
### wp profile queries
304+
305+
Profile database queries and their execution time.
306+
307+
~~~
308+
wp profile queries [--url=<url>] [--hook=<hook>] [--callback=<callback>] [--time_threshold=<seconds>] [--fields=<fields>] [--format=<format>] [--order=<order>] [--orderby=<fields>]
309+
~~~
310+
311+
Displays all database queries executed during a WordPress request,
312+
along with their execution time and caller information. You can filter
313+
queries to only show those executed during a specific hook or by a
314+
specific callback.
315+
316+
**OPTIONS**
317+
318+
[--url=<url>]
319+
Execute a request against a specified URL. Defaults to the home URL.
320+
321+
[--hook=<hook>]
322+
Filter queries to only show those executed during a specific hook.
323+
324+
[--callback=<callback>]
325+
Filter queries to only show those executed by a specific callback.
326+
327+
[--time_threshold=<seconds>]
328+
Filter queries to only show those that took longer than or equal to a certain number of seconds.
329+
330+
[--fields=<fields>]
331+
Limit the output to specific fields.
332+
333+
[--format=<format>]
334+
Render output in a particular format.
335+
---
336+
default: table
337+
options:
338+
- table
339+
- json
340+
- yaml
341+
- csv
342+
---
343+
344+
[--order=<order>]
345+
Ascending or Descending order.
346+
---
347+
default: ASC
348+
options:
349+
- ASC
350+
- DESC
351+
---
352+
353+
[--orderby=<fields>]
354+
Set orderby which field.
355+
356+
**EXAMPLES**
357+
358+
# Show all queries with their execution time
359+
$ wp profile queries --fields=query,time
360+
361+
# Show queries executed during the 'init' hook
362+
$ wp profile queries --hook=init --fields=query,time,caller
363+
364+
# Show queries executed by a specific callback
365+
$ wp profile queries --callback='WP_Query->get_posts()' --fields=query,time
366+
367+
# Show queries ordered by execution time
368+
$ wp profile queries --fields=query,time --orderby=time --order=DESC
369+
301370
## Installing
302371

303372
Installing this package requires WP-CLI v2.13 or greater. Update to the latest stable release with `wp cli update`.

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
"profile stage",
3030
"profile hook",
3131
"profile eval",
32-
"profile eval-file"
32+
"profile eval-file",
33+
"profile queries"
3334
],
3435
"readme": {
3536
"sections": [

features/profile-hook.feature

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ Feature: Profile a specific hook
3636
| total (0) |
3737
And STDERR should be empty
3838

39-
@require-wp-4.0
4039
Scenario: Profile a hook that has actions with output
4140
Given a WP install
4241

features/profile-queries.feature

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
Feature: Profile database queries
2+
3+
Scenario: Show all database queries
4+
Given a WP install
5+
And a wp-content/mu-plugins/test-queries.php file:
6+
"""
7+
<?php
8+
add_action( 'init', function() {
9+
global $wpdb;
10+
$wpdb->query( "SELECT 1 as test_query_one" );
11+
$wpdb->query( "SELECT 2 as test_query_two" );
12+
});
13+
"""
14+
15+
When I run `wp profile queries --fields=query,time`
16+
Then STDOUT should contain:
17+
"""
18+
query
19+
"""
20+
And STDOUT should contain:
21+
"""
22+
time
23+
"""
24+
And STDOUT should contain:
25+
"""
26+
SELECT 1 as test_query_one
27+
"""
28+
And STDOUT should contain:
29+
"""
30+
SELECT 2 as test_query_two
31+
"""
32+
And STDOUT should contain:
33+
"""
34+
total
35+
"""
36+
And STDERR should be empty
37+
38+
Scenario: Show queries with specific fields
39+
Given a WP install
40+
41+
When I run `wp profile queries --fields=query,time`
42+
Then STDOUT should contain:
43+
"""
44+
query
45+
"""
46+
And STDOUT should contain:
47+
"""
48+
time
49+
"""
50+
And STDOUT should contain:
51+
"""
52+
SELECT
53+
"""
54+
And STDERR should be empty
55+
56+
Scenario: Order queries by execution time
57+
Given a WP install
58+
59+
When I run `wp profile queries --fields=time --orderby=time --order=DESC`
60+
Then STDOUT should contain:
61+
"""
62+
time
63+
"""
64+
And STDERR should be empty
65+
66+
Scenario: Display queries in JSON format
67+
Given a WP install
68+
69+
When I run `wp profile queries --format=json --fields=query,time`
70+
Then STDOUT should contain:
71+
"""
72+
"query"
73+
"""
74+
And STDOUT should contain:
75+
"""
76+
"time"
77+
"""
78+
And STDERR should be empty
79+
80+
Scenario: Filter queries by hook
81+
Given a WP install
82+
And a wp-content/mu-plugins/query-test.php file:
83+
"""
84+
<?php
85+
add_action( 'init', function() {
86+
global $wpdb;
87+
$wpdb->query( "SELECT 1 as test_query" );
88+
});
89+
"""
90+
91+
When I run `wp profile queries --hook=init --fields=query,callback`
92+
Then STDOUT should contain:
93+
"""
94+
SELECT 1 as test_query
95+
"""
96+
And STDERR should be empty
97+
98+
Scenario: Filter queries by callback
99+
Given a WP install
100+
And a wp-content/mu-plugins/callback-test.php file:
101+
"""
102+
<?php
103+
function my_test_callback() {
104+
global $wpdb;
105+
$wpdb->query( "SELECT 2 as callback_test" );
106+
}
107+
add_action( 'init', 'my_test_callback' );
108+
"""
109+
110+
When I run `wp profile queries --callback=my_test_callback --fields=query,hook`
111+
Then STDOUT should contain:
112+
"""
113+
SELECT 2 as callback_test
114+
"""
115+
And STDERR should be empty
116+
117+
Scenario: Filter queries by time threshold
118+
Given a WP install
119+
And a wp-content/mu-plugins/test-threshold.php file:
120+
"""
121+
<?php
122+
add_action( 'init', function() {
123+
global $wpdb;
124+
$wpdb->queries[] = array( 'SELECT 1 as test_query_fast', 0.01, 'caller' );
125+
$wpdb->queries[] = array( 'SELECT 2 as test_query_slow', 0.2, 'caller' );
126+
});
127+
"""
128+
129+
When I run `wp profile queries --fields=query --time_threshold=0.1`
130+
Then STDOUT should contain:
131+
"""
132+
SELECT 2 as test_query_slow
133+
"""
134+
And STDOUT should not contain:
135+
"""
136+
SELECT 1 as test_query_fast
137+
"""
138+
And STDERR should be empty
139+
140+
Scenario: Format caller with newlines
141+
Given a WP install
142+
And a wp-content/mu-plugins/test-caller.php file:
143+
"""
144+
<?php
145+
function my_test_function() {
146+
global $wpdb;
147+
$wpdb->queries[] = array( 'SELECT 3 as test_caller', 0.01, 'frame1, frame2, frame3' );
148+
}
149+
add_action( 'init', 'my_test_function' );
150+
"""
151+
152+
When I run `wp profile queries --callback=my_test_function --fields=caller --format=json`
153+
Then STDOUT should be JSON containing:
154+
"""
155+
[
156+
{
157+
"caller": "frame1\nframe2\nframe3"
158+
}
159+
]
160+
"""
161+
And STDERR should be empty
162+
163+
Scenario: Format caller with newlines and strip WP-CLI frames
164+
Given a WP install
165+
And a wp-content/mu-plugins/test-caller-strip.php file:
166+
"""
167+
<?php
168+
function my_test_function_strip() {
169+
global $wpdb;
170+
$wpdb->queries[] = array( 'SELECT 4 as test_caller_strip_1', 0.01, 'WP_CLI\Main, WP_CLI\Profile\Profiler->load_wordpress_with_template(), frame1, frame2' );
171+
$wpdb->queries[] = array( 'SELECT 5 as test_caller_strip_2', 0.01, 'WP_CLI\Main, WP_CLI\Profile\Profiler->load_wordpress_with_template, frame3, frame4' );
172+
}
173+
add_action( 'init', 'my_test_function_strip' );
174+
"""
175+
176+
When I run `wp profile queries --callback=my_test_function_strip --fields=caller --format=json`
177+
Then STDOUT should be JSON containing:
178+
"""
179+
[
180+
{
181+
"caller": "frame1\nframe2"
182+
},
183+
{
184+
"caller": "frame3\nframe4"
185+
}
186+
]
187+
"""
188+
And STDERR should be empty

features/profile-stage.feature

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
Feature: Profile the template render stage
22

3-
@require-wp-4.0
43
Scenario: Profiler loads a summary table
54
Given a WP install
65

@@ -11,7 +10,6 @@ Feature: Profile the template render stage
1110
| main_query |
1211
| template |
1312

14-
@require-wp-4.0
1513
Scenario: Profiler loads a table with the correct hooks
1614
Given a WP install
1715

@@ -81,7 +79,6 @@ Feature: Profile the template render stage
8179
| loop_end:before |
8280
| loop_end |
8381

84-
@require-wp-4.0
8582
Scenario: Use --all flag to profile all stages
8683
Given a WP install
8784

@@ -134,7 +131,6 @@ Feature: Profile the template render stage
134131
Error: Invalid stage. Must be one of bootstrap, main_query, template, or use --all.
135132
"""
136133

137-
@require-wp-4.0
138134
Scenario: Invalid field name supplied to --fields
139135
Given a WP install
140136

@@ -145,7 +141,6 @@ Feature: Profile the template render stage
145141
"""
146142
And the return code should be 1
147143

148-
@require-wp-4.0
149144
Scenario: Identify callback_count for each hook
150145
Given a WP install
151146

@@ -154,7 +149,6 @@ Feature: Profile the template render stage
154149
| hook | callback_count |
155150
| muplugins_loaded | 2 |
156151

157-
@require-wp-4.0
158152
Scenario: Use spotlight mode to filter out the zero-ish values
159153
Given a WP install
160154

features/profile.feature

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Feature: Basic profile usage
99
usage: wp profile eval <php-code> [--hook[=<hook>]] [--fields=<fields>] [--format=<format>] [--order=<order>] [--orderby=<fields>]
1010
or: wp profile eval-file <file> [--hook[=<hook>]] [--fields=<fields>] [--format=<format>] [--order=<order>] [--orderby=<fields>]
1111
or: wp profile hook [<hook>] [--all] [--spotlight] [--url=<url>] [--fields=<fields>] [--format=<format>] [--order=<order>] [--orderby=<fields>] [--search=<pattern>]
12+
or: wp profile queries [--url=<url>] [--hook=<hook>] [--callback=<callback>] [--time_threshold=<seconds>] [--fields=<fields>] [--format=<format>] [--order=<order>] [--orderby=<fields>]
1213
or: wp profile stage [<stage>] [--all] [--spotlight] [--url=<url>] [--fields=<fields>] [--format=<format>] [--order=<order>] [--orderby=<fields>]
1314
1415
See 'wp help profile <command>' for more information on a specific command.
@@ -36,7 +37,6 @@ Feature: Basic profile usage
3637
Error: 'SAVEQUERIES' is defined as false, and must be true. Please check your wp-config.php
3738
"""
3839
39-
@require-wp-4.0
4040
Scenario: Profile a hook without any callbacks
4141
Given a WP install
4242
@@ -46,7 +46,6 @@ Feature: Basic profile usage
4646
| total (0) | |
4747
And STDERR should be empty
4848
49-
@require-wp-4.0
5049
Scenario: Trailingslash provided URL to avoid canonical redirect
5150
Given a WP install
5251

0 commit comments

Comments
 (0)