@mytechtoday/augment-extensions 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/augment-extensions/domain-rules/wordpress/README.md +163 -0
- package/augment-extensions/domain-rules/wordpress/module.json +32 -0
- package/augment-extensions/domain-rules/wordpress/rules/coding-standards.md +617 -0
- package/augment-extensions/domain-rules/wordpress/rules/directory-structure.md +270 -0
- package/augment-extensions/domain-rules/wordpress/rules/file-patterns.md +423 -0
- package/augment-extensions/domain-rules/wordpress/rules/gutenberg-blocks.md +493 -0
- package/augment-extensions/domain-rules/wordpress/rules/performance.md +568 -0
- package/augment-extensions/domain-rules/wordpress/rules/plugin-development.md +510 -0
- package/augment-extensions/domain-rules/wordpress/rules/project-detection.md +251 -0
- package/augment-extensions/domain-rules/wordpress/rules/rest-api.md +501 -0
- package/augment-extensions/domain-rules/wordpress/rules/security.md +564 -0
- package/augment-extensions/domain-rules/wordpress/rules/theme-development.md +388 -0
- package/augment-extensions/domain-rules/wordpress/rules/woocommerce.md +441 -0
- package/augment-extensions/domain-rules/wordpress-plugin/README.md +139 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/ajax-plugin.md +1599 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/custom-post-type-plugin.md +1727 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/gutenberg-block-plugin.md +428 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/gutenberg-block.md +422 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/mvc-plugin.md +1623 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/object-oriented-plugin.md +1343 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/rest-endpoint.md +734 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/settings-page-plugin.md +1350 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/simple-procedural-plugin.md +503 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/singleton-plugin.md +971 -0
- package/augment-extensions/domain-rules/wordpress-plugin/module.json +53 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/activation-hooks.md +770 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/admin-interface.md +874 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/ajax-handlers.md +629 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/asset-management.md +559 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/context-providers.md +709 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/cron-jobs.md +736 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/database-management.md +1057 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/documentation-standards.md +463 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/frontend-functionality.md +478 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/gutenberg-blocks.md +818 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/internationalization.md +416 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/migration.md +667 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/performance-optimization.md +878 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/plugin-architecture.md +693 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/plugin-structure.md +352 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/rest-api.md +818 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/scaffolding-workflow.md +624 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/security-best-practices.md +866 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/testing-patterns.md +1165 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/testing.md +414 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/vscode-integration.md +751 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/woocommerce-integration.md +949 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/wordpress-org-submission.md +458 -0
- package/augment-extensions/examples/gutenberg-block-plugin/README.md +101 -0
- package/augment-extensions/examples/gutenberg-block-plugin/examples/testimonial-block.md +428 -0
- package/augment-extensions/examples/gutenberg-block-plugin/module.json +40 -0
- package/augment-extensions/examples/rest-api-plugin/README.md +98 -0
- package/augment-extensions/examples/rest-api-plugin/examples/task-manager-api.md +1299 -0
- package/augment-extensions/examples/rest-api-plugin/module.json +40 -0
- package/augment-extensions/examples/woocommerce-extension/README.md +98 -0
- package/augment-extensions/examples/woocommerce-extension/examples/product-customizer.md +763 -0
- package/augment-extensions/examples/woocommerce-extension/module.json +40 -0
- package/augment-extensions/workflows/wordpress-plugin/README.md +232 -0
- package/augment-extensions/workflows/wordpress-plugin/ai-prompts.md +839 -0
- package/augment-extensions/workflows/wordpress-plugin/bead-decomposition-patterns.md +854 -0
- package/augment-extensions/workflows/wordpress-plugin/examples/complete-plugin-example.md +540 -0
- package/augment-extensions/workflows/wordpress-plugin/examples/custom-post-type-example.md +1083 -0
- package/augment-extensions/workflows/wordpress-plugin/examples/feature-addition-workflow.md +669 -0
- package/augment-extensions/workflows/wordpress-plugin/examples/plugin-creation-workflow.md +597 -0
- package/augment-extensions/workflows/wordpress-plugin/examples/secure-form-handler-example.md +925 -0
- package/augment-extensions/workflows/wordpress-plugin/examples/security-audit-workflow.md +752 -0
- package/augment-extensions/workflows/wordpress-plugin/examples/wordpress-org-submission-workflow.md +773 -0
- package/augment-extensions/workflows/wordpress-plugin/module.json +49 -0
- package/augment-extensions/workflows/wordpress-plugin/rules/best-practices.md +942 -0
- package/augment-extensions/workflows/wordpress-plugin/rules/development-workflow.md +702 -0
- package/augment-extensions/workflows/wordpress-plugin/rules/submission-workflow.md +728 -0
- package/augment-extensions/workflows/wordpress-plugin/rules/testing-workflow.md +775 -0
- package/cli/dist/cli.js +5 -1
- package/cli/dist/cli.js.map +1 -1
- package/cli/dist/commands/show.d.ts.map +1 -1
- package/cli/dist/commands/show.js +41 -0
- package/cli/dist/commands/show.js.map +1 -1
- package/modules.md +52 -0
- package/package.json +1 -1
|
@@ -0,0 +1,878 @@
|
|
|
1
|
+
# Performance Optimization
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This guide covers performance best practices for WordPress plugins including transient caching, object caching, query optimization, lazy loading, and asset minification.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Transient Caching
|
|
10
|
+
|
|
11
|
+
### Basic Transient Usage
|
|
12
|
+
|
|
13
|
+
```php
|
|
14
|
+
<?php
|
|
15
|
+
/**
|
|
16
|
+
* Get data with transient caching
|
|
17
|
+
*/
|
|
18
|
+
function my_plugin_get_popular_posts() {
|
|
19
|
+
$cache_key = 'my_plugin_popular_posts';
|
|
20
|
+
|
|
21
|
+
// Try to get cached data
|
|
22
|
+
$popular_posts = get_transient( $cache_key );
|
|
23
|
+
|
|
24
|
+
if ( false === $popular_posts ) {
|
|
25
|
+
// Expensive query
|
|
26
|
+
$popular_posts = new WP_Query( array(
|
|
27
|
+
'post_type' => 'post',
|
|
28
|
+
'posts_per_page' => 10,
|
|
29
|
+
'meta_key' => 'views',
|
|
30
|
+
'orderby' => 'meta_value_num',
|
|
31
|
+
'order' => 'DESC',
|
|
32
|
+
) );
|
|
33
|
+
|
|
34
|
+
// Cache for 1 hour
|
|
35
|
+
set_transient( $cache_key, $popular_posts, HOUR_IN_SECONDS );
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return $popular_posts;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Clear cache when post is updated
|
|
43
|
+
*/
|
|
44
|
+
function my_plugin_clear_cache( $post_id ) {
|
|
45
|
+
delete_transient( 'my_plugin_popular_posts' );
|
|
46
|
+
}
|
|
47
|
+
add_action( 'save_post', 'my_plugin_clear_cache' );
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Transient Expiration Times
|
|
51
|
+
|
|
52
|
+
```php
|
|
53
|
+
<?php
|
|
54
|
+
// WordPress time constants
|
|
55
|
+
MINUTE_IN_SECONDS // 60 seconds
|
|
56
|
+
HOUR_IN_SECONDS // 3600 seconds
|
|
57
|
+
DAY_IN_SECONDS // 86400 seconds
|
|
58
|
+
WEEK_IN_SECONDS // 604800 seconds
|
|
59
|
+
MONTH_IN_SECONDS // 2592000 seconds (30 days)
|
|
60
|
+
YEAR_IN_SECONDS // 31536000 seconds
|
|
61
|
+
|
|
62
|
+
// Examples
|
|
63
|
+
set_transient( 'key', $data, 5 * MINUTE_IN_SECONDS ); // 5 minutes
|
|
64
|
+
set_transient( 'key', $data, 12 * HOUR_IN_SECONDS ); // 12 hours
|
|
65
|
+
set_transient( 'key', $data, 7 * DAY_IN_SECONDS ); // 7 days
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Transient Best Practices
|
|
69
|
+
|
|
70
|
+
```php
|
|
71
|
+
<?php
|
|
72
|
+
/**
|
|
73
|
+
* Cache expensive API call
|
|
74
|
+
*/
|
|
75
|
+
function my_plugin_get_api_data() {
|
|
76
|
+
$cache_key = 'my_plugin_api_data';
|
|
77
|
+
$data = get_transient( $cache_key );
|
|
78
|
+
|
|
79
|
+
if ( false === $data ) {
|
|
80
|
+
$response = wp_remote_get( 'https://api.example.com/data' );
|
|
81
|
+
|
|
82
|
+
if ( is_wp_error( $response ) ) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
$data = wp_remote_retrieve_body( $response );
|
|
87
|
+
|
|
88
|
+
// Cache for 1 hour
|
|
89
|
+
set_transient( $cache_key, $data, HOUR_IN_SECONDS );
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return $data;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Clear all plugin transients
|
|
97
|
+
*/
|
|
98
|
+
function my_plugin_clear_all_transients() {
|
|
99
|
+
global $wpdb;
|
|
100
|
+
|
|
101
|
+
$wpdb->query(
|
|
102
|
+
"DELETE FROM {$wpdb->options}
|
|
103
|
+
WHERE option_name LIKE '_transient_my_plugin_%'
|
|
104
|
+
OR option_name LIKE '_transient_timeout_my_plugin_%'"
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Object Caching
|
|
112
|
+
|
|
113
|
+
### WordPress Object Cache
|
|
114
|
+
|
|
115
|
+
```php
|
|
116
|
+
<?php
|
|
117
|
+
/**
|
|
118
|
+
* Get data with object caching
|
|
119
|
+
*/
|
|
120
|
+
function my_plugin_get_user_data( $user_id ) {
|
|
121
|
+
$cache_key = 'user_data_' . $user_id;
|
|
122
|
+
$cache_group = 'my_plugin';
|
|
123
|
+
|
|
124
|
+
// Try to get from cache
|
|
125
|
+
$user_data = wp_cache_get( $cache_key, $cache_group );
|
|
126
|
+
|
|
127
|
+
if ( false === $user_data ) {
|
|
128
|
+
// Expensive operation
|
|
129
|
+
$user_data = array(
|
|
130
|
+
'name' => get_user_meta( $user_id, 'name', true ),
|
|
131
|
+
'email' => get_user_meta( $user_id, 'email', true ),
|
|
132
|
+
'posts' => count_user_posts( $user_id ),
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
// Cache for 5 minutes
|
|
136
|
+
wp_cache_set( $cache_key, $user_data, $cache_group, 300 );
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return $user_data;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Delete from cache
|
|
144
|
+
*/
|
|
145
|
+
function my_plugin_clear_user_cache( $user_id ) {
|
|
146
|
+
$cache_key = 'user_data_' . $user_id;
|
|
147
|
+
wp_cache_delete( $cache_key, 'my_plugin' );
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Cache Groups
|
|
152
|
+
|
|
153
|
+
```php
|
|
154
|
+
<?php
|
|
155
|
+
// Set cache with group
|
|
156
|
+
wp_cache_set( 'key', $data, 'my_plugin', 3600 );
|
|
157
|
+
|
|
158
|
+
// Get cache from group
|
|
159
|
+
$data = wp_cache_get( 'key', 'my_plugin' );
|
|
160
|
+
|
|
161
|
+
// Delete cache from group
|
|
162
|
+
wp_cache_delete( 'key', 'my_plugin' );
|
|
163
|
+
|
|
164
|
+
// Flush entire group (if supported by cache backend)
|
|
165
|
+
wp_cache_flush_group( 'my_plugin' );
|
|
166
|
+
|
|
167
|
+
// Flush all cache
|
|
168
|
+
wp_cache_flush();
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Query Optimization
|
|
174
|
+
|
|
175
|
+
### Avoid N+1 Queries
|
|
176
|
+
|
|
177
|
+
```php
|
|
178
|
+
<?php
|
|
179
|
+
// ❌ BAD: Query in loop (N+1 problem)
|
|
180
|
+
foreach ( $post_ids as $post_id ) {
|
|
181
|
+
$post = get_post( $post_id ); // Separate query for each post
|
|
182
|
+
echo $post->post_title;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ✅ GOOD: Single query
|
|
186
|
+
$posts = get_posts( array(
|
|
187
|
+
'post__in' => $post_ids,
|
|
188
|
+
'posts_per_page' => -1,
|
|
189
|
+
) );
|
|
190
|
+
foreach ( $posts as $post ) {
|
|
191
|
+
echo $post->post_title;
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Optimize WP_Query
|
|
196
|
+
|
|
197
|
+
```php
|
|
198
|
+
<?php
|
|
199
|
+
// ✅ GOOD: Limit fields to what you need
|
|
200
|
+
$query = new WP_Query( array(
|
|
201
|
+
'post_type' => 'post',
|
|
202
|
+
'posts_per_page' => 10,
|
|
203
|
+
'fields' => 'ids', // Only get IDs
|
|
204
|
+
) );
|
|
205
|
+
|
|
206
|
+
// ✅ GOOD: Disable unnecessary features
|
|
207
|
+
$query = new WP_Query( array(
|
|
208
|
+
'post_type' => 'post',
|
|
209
|
+
'posts_per_page' => 10,
|
|
210
|
+
'no_found_rows' => true, // Disable pagination count
|
|
211
|
+
'update_post_meta_cache' => false, // Don't cache post meta
|
|
212
|
+
'update_post_term_cache' => false, // Don't cache terms
|
|
213
|
+
) );
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Optimize Meta Queries
|
|
217
|
+
|
|
218
|
+
```php
|
|
219
|
+
<?php
|
|
220
|
+
// ❌ BAD: Multiple meta queries (slow)
|
|
221
|
+
$query = new WP_Query( array(
|
|
222
|
+
'meta_query' => array(
|
|
223
|
+
array(
|
|
224
|
+
'key' => 'color',
|
|
225
|
+
'value' => 'blue',
|
|
226
|
+
),
|
|
227
|
+
array(
|
|
228
|
+
'key' => 'size',
|
|
229
|
+
'value' => 'large',
|
|
230
|
+
),
|
|
231
|
+
),
|
|
232
|
+
) );
|
|
233
|
+
|
|
234
|
+
// ✅ GOOD: Use custom taxonomy instead
|
|
235
|
+
register_taxonomy( 'product_color', 'product' );
|
|
236
|
+
register_taxonomy( 'product_size', 'product' );
|
|
237
|
+
|
|
238
|
+
$query = new WP_Query( array(
|
|
239
|
+
'post_type' => 'product',
|
|
240
|
+
'tax_query' => array(
|
|
241
|
+
array(
|
|
242
|
+
'taxonomy' => 'product_color',
|
|
243
|
+
'field' => 'slug',
|
|
244
|
+
'terms' => 'blue',
|
|
245
|
+
),
|
|
246
|
+
array(
|
|
247
|
+
'taxonomy' => 'product_size',
|
|
248
|
+
'field' => 'slug',
|
|
249
|
+
'terms' => 'large',
|
|
250
|
+
),
|
|
251
|
+
),
|
|
252
|
+
) );
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Database Indexes
|
|
256
|
+
|
|
257
|
+
```php
|
|
258
|
+
<?php
|
|
259
|
+
/**
|
|
260
|
+
* Create custom table with indexes
|
|
261
|
+
*/
|
|
262
|
+
function my_plugin_create_table() {
|
|
263
|
+
global $wpdb;
|
|
264
|
+
|
|
265
|
+
$table_name = $wpdb->prefix . 'my_plugin_data';
|
|
266
|
+
$charset_collate = $wpdb->get_charset_collate();
|
|
267
|
+
|
|
268
|
+
$sql = "CREATE TABLE $table_name (
|
|
269
|
+
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
|
270
|
+
user_id bigint(20) unsigned NOT NULL,
|
|
271
|
+
status varchar(20) NOT NULL,
|
|
272
|
+
created_at datetime DEFAULT CURRENT_TIMESTAMP,
|
|
273
|
+
PRIMARY KEY (id),
|
|
274
|
+
KEY user_id (user_id),
|
|
275
|
+
KEY status (status),
|
|
276
|
+
KEY created_at (created_at)
|
|
277
|
+
) $charset_collate;";
|
|
278
|
+
|
|
279
|
+
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
|
|
280
|
+
dbDelta( $sql );
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## Asset Optimization
|
|
287
|
+
|
|
288
|
+
### Conditional Script Loading
|
|
289
|
+
|
|
290
|
+
```php
|
|
291
|
+
<?php
|
|
292
|
+
/**
|
|
293
|
+
* Enqueue scripts only where needed
|
|
294
|
+
*/
|
|
295
|
+
function my_plugin_enqueue_scripts() {
|
|
296
|
+
// Only load on single posts
|
|
297
|
+
if ( is_single() ) {
|
|
298
|
+
wp_enqueue_script(
|
|
299
|
+
'my-plugin-single',
|
|
300
|
+
plugins_url( 'js/single.js', __FILE__ ),
|
|
301
|
+
array( 'jquery' ),
|
|
302
|
+
'1.0.0',
|
|
303
|
+
true // Load in footer
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Only load on specific page template
|
|
308
|
+
if ( is_page_template( 'template-contact.php' ) ) {
|
|
309
|
+
wp_enqueue_script(
|
|
310
|
+
'my-plugin-contact',
|
|
311
|
+
plugins_url( 'js/contact.js', __FILE__ ),
|
|
312
|
+
array(),
|
|
313
|
+
'1.0.0',
|
|
314
|
+
true
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Only load in admin
|
|
319
|
+
if ( is_admin() ) {
|
|
320
|
+
wp_enqueue_script(
|
|
321
|
+
'my-plugin-admin',
|
|
322
|
+
plugins_url( 'js/admin.js', __FILE__ ),
|
|
323
|
+
array( 'jquery' ),
|
|
324
|
+
'1.0.0',
|
|
325
|
+
true
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
add_action( 'wp_enqueue_scripts', 'my_plugin_enqueue_scripts' );
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### Minify Assets
|
|
333
|
+
|
|
334
|
+
```php
|
|
335
|
+
<?php
|
|
336
|
+
/**
|
|
337
|
+
* Enqueue minified assets in production
|
|
338
|
+
*/
|
|
339
|
+
function my_plugin_enqueue_assets() {
|
|
340
|
+
$suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
|
|
341
|
+
|
|
342
|
+
wp_enqueue_style(
|
|
343
|
+
'my-plugin-style',
|
|
344
|
+
plugins_url( "css/style{$suffix}.css", __FILE__ ),
|
|
345
|
+
array(),
|
|
346
|
+
'1.0.0'
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
wp_enqueue_script(
|
|
350
|
+
'my-plugin-script',
|
|
351
|
+
plugins_url( "js/main{$suffix}.js", __FILE__ ),
|
|
352
|
+
array( 'jquery' ),
|
|
353
|
+
'1.0.0',
|
|
354
|
+
true
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
add_action( 'wp_enqueue_scripts', 'my_plugin_enqueue_assets' );
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Defer and Async Loading
|
|
361
|
+
|
|
362
|
+
```php
|
|
363
|
+
<?php
|
|
364
|
+
/**
|
|
365
|
+
* Add defer/async to scripts
|
|
366
|
+
*/
|
|
367
|
+
function my_plugin_add_defer_async( $tag, $handle ) {
|
|
368
|
+
// Add defer to specific scripts
|
|
369
|
+
$defer_scripts = array( 'my-plugin-analytics', 'my-plugin-tracking' );
|
|
370
|
+
|
|
371
|
+
if ( in_array( $handle, $defer_scripts, true ) ) {
|
|
372
|
+
return str_replace( ' src', ' defer src', $tag );
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Add async to specific scripts
|
|
376
|
+
$async_scripts = array( 'my-plugin-ads' );
|
|
377
|
+
|
|
378
|
+
if ( in_array( $handle, $async_scripts, true ) ) {
|
|
379
|
+
return str_replace( ' src', ' async src', $tag );
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
return $tag;
|
|
383
|
+
}
|
|
384
|
+
add_filter( 'script_loader_tag', 'my_plugin_add_defer_async', 10, 2 );
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
---
|
|
388
|
+
|
|
389
|
+
## Lazy Loading
|
|
390
|
+
|
|
391
|
+
### Lazy Load Images
|
|
392
|
+
|
|
393
|
+
```php
|
|
394
|
+
<?php
|
|
395
|
+
/**
|
|
396
|
+
* Add lazy loading to images (WordPress 5.5+)
|
|
397
|
+
*/
|
|
398
|
+
function my_plugin_add_lazy_loading( $content ) {
|
|
399
|
+
if ( is_admin() ) {
|
|
400
|
+
return $content;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// WordPress 5.5+ has native lazy loading
|
|
404
|
+
// This adds it to older versions
|
|
405
|
+
$content = preg_replace(
|
|
406
|
+
'/<img(.*?)src=/i',
|
|
407
|
+
'<img$1loading="lazy" src=',
|
|
408
|
+
$content
|
|
409
|
+
);
|
|
410
|
+
|
|
411
|
+
return $content;
|
|
412
|
+
}
|
|
413
|
+
add_filter( 'the_content', 'my_plugin_add_lazy_loading' );
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
### Lazy Load Components
|
|
417
|
+
|
|
418
|
+
```php
|
|
419
|
+
<?php
|
|
420
|
+
/**
|
|
421
|
+
* Load component only when needed
|
|
422
|
+
*/
|
|
423
|
+
function my_plugin_load_component() {
|
|
424
|
+
// Check if component is needed
|
|
425
|
+
if ( ! is_page( 'special-page' ) ) {
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Load component files only when needed
|
|
430
|
+
require_once plugin_dir_path( __FILE__ ) . 'includes/class-special-component.php';
|
|
431
|
+
|
|
432
|
+
$component = new My_Plugin_Special_Component();
|
|
433
|
+
$component->init();
|
|
434
|
+
}
|
|
435
|
+
add_action( 'wp', 'my_plugin_load_component' );
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
---
|
|
439
|
+
|
|
440
|
+
## Autoloaded Options
|
|
441
|
+
|
|
442
|
+
### Optimize Autoloaded Data
|
|
443
|
+
|
|
444
|
+
```php
|
|
445
|
+
<?php
|
|
446
|
+
/**
|
|
447
|
+
* Don't autoload large options
|
|
448
|
+
*/
|
|
449
|
+
// ❌ BAD: Autoloads large data on every page load
|
|
450
|
+
update_option( 'my_plugin_large_data', $large_array );
|
|
451
|
+
|
|
452
|
+
// ✅ GOOD: Don't autoload large data
|
|
453
|
+
update_option( 'my_plugin_large_data', $large_array, false ); // false = don't autoload
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Check autoloaded data size
|
|
457
|
+
*/
|
|
458
|
+
function my_plugin_check_autoloaded_options() {
|
|
459
|
+
global $wpdb;
|
|
460
|
+
|
|
461
|
+
$autoloaded_options = $wpdb->get_results(
|
|
462
|
+
"SELECT option_name, LENGTH(option_value) as option_size
|
|
463
|
+
FROM {$wpdb->options}
|
|
464
|
+
WHERE autoload = 'yes'
|
|
465
|
+
ORDER BY option_size DESC
|
|
466
|
+
LIMIT 20"
|
|
467
|
+
);
|
|
468
|
+
|
|
469
|
+
foreach ( $autoloaded_options as $option ) {
|
|
470
|
+
echo $option->option_name . ': ' . size_format( $option->option_size ) . "\n";
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
---
|
|
476
|
+
|
|
477
|
+
## HTTP Requests
|
|
478
|
+
|
|
479
|
+
### Reduce External Requests
|
|
480
|
+
|
|
481
|
+
```php
|
|
482
|
+
<?php
|
|
483
|
+
/**
|
|
484
|
+
* Remove unnecessary scripts
|
|
485
|
+
*/
|
|
486
|
+
function my_plugin_remove_unnecessary_scripts() {
|
|
487
|
+
// Remove emoji scripts
|
|
488
|
+
remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
|
|
489
|
+
remove_action( 'wp_print_styles', 'print_emoji_styles' );
|
|
490
|
+
|
|
491
|
+
// Remove embed script
|
|
492
|
+
remove_action( 'wp_head', 'wp_oembed_add_discovery_links' );
|
|
493
|
+
remove_action( 'wp_head', 'wp_oembed_add_host_js' );
|
|
494
|
+
}
|
|
495
|
+
add_action( 'init', 'my_plugin_remove_unnecessary_scripts' );
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
### Batch API Requests
|
|
499
|
+
|
|
500
|
+
```php
|
|
501
|
+
<?php
|
|
502
|
+
/**
|
|
503
|
+
* Batch multiple API requests
|
|
504
|
+
*/
|
|
505
|
+
function my_plugin_batch_api_requests( $endpoints ) {
|
|
506
|
+
$responses = array();
|
|
507
|
+
|
|
508
|
+
foreach ( $endpoints as $key => $url ) {
|
|
509
|
+
$response = wp_remote_get( $url, array(
|
|
510
|
+
'timeout' => 10,
|
|
511
|
+
) );
|
|
512
|
+
|
|
513
|
+
if ( ! is_wp_error( $response ) ) {
|
|
514
|
+
$responses[ $key ] = wp_remote_retrieve_body( $response );
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
return $responses;
|
|
519
|
+
}
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
---
|
|
523
|
+
|
|
524
|
+
## Monitoring and Profiling
|
|
525
|
+
|
|
526
|
+
### Query Monitor Plugin
|
|
527
|
+
|
|
528
|
+
```php
|
|
529
|
+
<?php
|
|
530
|
+
/**
|
|
531
|
+
* Add custom timing with Query Monitor
|
|
532
|
+
*/
|
|
533
|
+
function my_plugin_expensive_operation() {
|
|
534
|
+
do_action( 'qm/start', 'my_expensive_operation' );
|
|
535
|
+
|
|
536
|
+
// Expensive operation here
|
|
537
|
+
my_plugin_process_data();
|
|
538
|
+
|
|
539
|
+
do_action( 'qm/stop', 'my_expensive_operation' );
|
|
540
|
+
}
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
### Debug Queries
|
|
544
|
+
|
|
545
|
+
```php
|
|
546
|
+
<?php
|
|
547
|
+
/**
|
|
548
|
+
* Enable query debugging (development only)
|
|
549
|
+
* Add to wp-config.php
|
|
550
|
+
*/
|
|
551
|
+
define( 'SAVEQUERIES', true );
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Display queries (admin only)
|
|
555
|
+
*/
|
|
556
|
+
function my_plugin_display_queries() {
|
|
557
|
+
if ( ! current_user_can( 'manage_options' ) || ! defined( 'SAVEQUERIES' ) || ! SAVEQUERIES ) {
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
global $wpdb;
|
|
562
|
+
|
|
563
|
+
echo '<h2>Database Queries</h2>';
|
|
564
|
+
echo '<p>Total queries: ' . count( $wpdb->queries ) . '</p>';
|
|
565
|
+
|
|
566
|
+
foreach ( $wpdb->queries as $query ) {
|
|
567
|
+
echo '<pre>';
|
|
568
|
+
echo 'Query: ' . $query[0] . "\n";
|
|
569
|
+
echo 'Time: ' . $query[1] . " seconds\n";
|
|
570
|
+
echo 'Called by: ' . $query[2] . "\n";
|
|
571
|
+
echo '</pre>';
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
add_action( 'wp_footer', 'my_plugin_display_queries' );
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
### Performance Logging
|
|
578
|
+
|
|
579
|
+
```php
|
|
580
|
+
<?php
|
|
581
|
+
/**
|
|
582
|
+
* Log slow operations
|
|
583
|
+
*/
|
|
584
|
+
function my_plugin_log_performance( $operation_name, $start_time ) {
|
|
585
|
+
$end_time = microtime( true );
|
|
586
|
+
$duration = $end_time - $start_time;
|
|
587
|
+
|
|
588
|
+
// Log if operation took more than 1 second
|
|
589
|
+
if ( $duration > 1.0 ) {
|
|
590
|
+
error_log( sprintf(
|
|
591
|
+
'Slow operation: %s took %.2f seconds',
|
|
592
|
+
$operation_name,
|
|
593
|
+
$duration
|
|
594
|
+
) );
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* Example usage
|
|
600
|
+
*/
|
|
601
|
+
function my_plugin_process_data() {
|
|
602
|
+
$start_time = microtime( true );
|
|
603
|
+
|
|
604
|
+
// Process data
|
|
605
|
+
// ...
|
|
606
|
+
|
|
607
|
+
my_plugin_log_performance( 'process_data', $start_time );
|
|
608
|
+
}
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
---
|
|
612
|
+
|
|
613
|
+
## Best Practices
|
|
614
|
+
|
|
615
|
+
### Caching
|
|
616
|
+
|
|
617
|
+
✅ **DO**:
|
|
618
|
+
- Cache expensive queries with transients
|
|
619
|
+
- Use object caching for frequently accessed data
|
|
620
|
+
- Clear cache when data changes
|
|
621
|
+
- Set appropriate expiration times
|
|
622
|
+
- Use cache groups for organization
|
|
623
|
+
|
|
624
|
+
❌ **DON'T**:
|
|
625
|
+
- Cache everything indiscriminately
|
|
626
|
+
- Forget to clear cache on updates
|
|
627
|
+
- Use very short expiration times (defeats purpose)
|
|
628
|
+
- Cache user-specific data in shared cache
|
|
629
|
+
|
|
630
|
+
```php
|
|
631
|
+
<?php
|
|
632
|
+
// ✅ GOOD: Cache with appropriate expiration
|
|
633
|
+
function my_plugin_get_stats() {
|
|
634
|
+
$cache_key = 'my_plugin_stats';
|
|
635
|
+
$stats = get_transient( $cache_key );
|
|
636
|
+
|
|
637
|
+
if ( false === $stats ) {
|
|
638
|
+
$stats = my_plugin_calculate_stats();
|
|
639
|
+
set_transient( $cache_key, $stats, HOUR_IN_SECONDS );
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
return $stats;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// ✅ GOOD: Clear cache on update
|
|
646
|
+
function my_plugin_clear_stats_cache() {
|
|
647
|
+
delete_transient( 'my_plugin_stats' );
|
|
648
|
+
}
|
|
649
|
+
add_action( 'save_post', 'my_plugin_clear_stats_cache' );
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
### Database Queries
|
|
653
|
+
|
|
654
|
+
✅ **DO**:
|
|
655
|
+
- Use indexes on frequently queried columns
|
|
656
|
+
- Limit query results with LIMIT
|
|
657
|
+
- Use `fields` parameter to get only needed data
|
|
658
|
+
- Disable unnecessary caching in WP_Query
|
|
659
|
+
- Use prepared statements
|
|
660
|
+
|
|
661
|
+
❌ **DON'T**:
|
|
662
|
+
- Query in loops (N+1 problem)
|
|
663
|
+
- Fetch all posts without pagination
|
|
664
|
+
- Use complex meta queries
|
|
665
|
+
- Ignore slow query warnings
|
|
666
|
+
|
|
667
|
+
```php
|
|
668
|
+
<?php
|
|
669
|
+
// ✅ GOOD: Optimized query
|
|
670
|
+
$posts = get_posts( array(
|
|
671
|
+
'post_type' => 'post',
|
|
672
|
+
'posts_per_page' => 10,
|
|
673
|
+
'fields' => 'ids',
|
|
674
|
+
'no_found_rows' => true,
|
|
675
|
+
'update_post_meta_cache' => false,
|
|
676
|
+
'update_post_term_cache' => false,
|
|
677
|
+
) );
|
|
678
|
+
|
|
679
|
+
// ❌ BAD: Unoptimized query
|
|
680
|
+
$posts = get_posts( array(
|
|
681
|
+
'post_type' => 'post',
|
|
682
|
+
'posts_per_page' => -1, // Gets ALL posts
|
|
683
|
+
) );
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
### Assets
|
|
687
|
+
|
|
688
|
+
✅ **DO**:
|
|
689
|
+
- Load scripts only where needed
|
|
690
|
+
- Minify CSS and JavaScript
|
|
691
|
+
- Use defer/async for non-critical scripts
|
|
692
|
+
- Load scripts in footer
|
|
693
|
+
- Combine files when possible
|
|
694
|
+
|
|
695
|
+
❌ **DON'T**:
|
|
696
|
+
- Load all scripts on every page
|
|
697
|
+
- Enqueue unminified files in production
|
|
698
|
+
- Load large scripts in header
|
|
699
|
+
- Make excessive HTTP requests
|
|
700
|
+
|
|
701
|
+
```php
|
|
702
|
+
<?php
|
|
703
|
+
// ✅ GOOD: Conditional loading
|
|
704
|
+
function my_plugin_enqueue_scripts() {
|
|
705
|
+
if ( is_single() ) {
|
|
706
|
+
wp_enqueue_script(
|
|
707
|
+
'my-plugin-single',
|
|
708
|
+
plugins_url( 'js/single.min.js', __FILE__ ),
|
|
709
|
+
array( 'jquery' ),
|
|
710
|
+
'1.0.0',
|
|
711
|
+
true // Load in footer
|
|
712
|
+
);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
add_action( 'wp_enqueue_scripts', 'my_plugin_enqueue_scripts' );
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
---
|
|
719
|
+
|
|
720
|
+
## Common Pitfalls
|
|
721
|
+
|
|
722
|
+
### ❌ DON'T
|
|
723
|
+
|
|
724
|
+
```php
|
|
725
|
+
<?php
|
|
726
|
+
// Don't query in loops
|
|
727
|
+
foreach ( $post_ids as $post_id ) {
|
|
728
|
+
$post = get_post( $post_id ); // WRONG - N+1 queries
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// Don't fetch all posts
|
|
732
|
+
$posts = get_posts( array(
|
|
733
|
+
'posts_per_page' => -1, // WRONG - Gets ALL posts
|
|
734
|
+
) );
|
|
735
|
+
|
|
736
|
+
// Don't autoload large data
|
|
737
|
+
update_option( 'my_large_data', $huge_array ); // WRONG - Autoloads by default
|
|
738
|
+
|
|
739
|
+
// Don't skip caching
|
|
740
|
+
function get_expensive_data() {
|
|
741
|
+
return expensive_calculation(); // WRONG - No caching
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// Don't load scripts everywhere
|
|
745
|
+
function my_plugin_enqueue() {
|
|
746
|
+
wp_enqueue_script( 'my-script', ... ); // WRONG - Loads on every page
|
|
747
|
+
}
|
|
748
|
+
add_action( 'wp_enqueue_scripts', 'my_plugin_enqueue' );
|
|
749
|
+
|
|
750
|
+
// Don't ignore indexes
|
|
751
|
+
$sql = "CREATE TABLE $table (
|
|
752
|
+
id bigint(20) NOT NULL AUTO_INCREMENT,
|
|
753
|
+
user_id bigint(20) NOT NULL,
|
|
754
|
+
PRIMARY KEY (id)
|
|
755
|
+
)"; // WRONG - Missing index on user_id
|
|
756
|
+
|
|
757
|
+
// Don't cache forever
|
|
758
|
+
set_transient( 'key', $data, YEAR_IN_SECONDS ); // WRONG - Too long
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
### ✅ DO
|
|
762
|
+
|
|
763
|
+
```php
|
|
764
|
+
<?php
|
|
765
|
+
// Fetch posts in single query
|
|
766
|
+
$posts = get_posts( array(
|
|
767
|
+
'post__in' => $post_ids,
|
|
768
|
+
'posts_per_page' => -1,
|
|
769
|
+
) ); // CORRECT
|
|
770
|
+
|
|
771
|
+
// Limit query results
|
|
772
|
+
$posts = get_posts( array(
|
|
773
|
+
'posts_per_page' => 10,
|
|
774
|
+
) ); // CORRECT
|
|
775
|
+
|
|
776
|
+
// Don't autoload large data
|
|
777
|
+
update_option( 'my_large_data', $huge_array, false ); // CORRECT
|
|
778
|
+
|
|
779
|
+
// Cache expensive operations
|
|
780
|
+
function get_expensive_data() {
|
|
781
|
+
$cache_key = 'expensive_data';
|
|
782
|
+
$data = get_transient( $cache_key );
|
|
783
|
+
|
|
784
|
+
if ( false === $data ) {
|
|
785
|
+
$data = expensive_calculation();
|
|
786
|
+
set_transient( $cache_key, $data, HOUR_IN_SECONDS );
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
return $data;
|
|
790
|
+
} // CORRECT
|
|
791
|
+
|
|
792
|
+
// Load scripts conditionally
|
|
793
|
+
function my_plugin_enqueue() {
|
|
794
|
+
if ( is_single() ) {
|
|
795
|
+
wp_enqueue_script( 'my-script', ... );
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
add_action( 'wp_enqueue_scripts', 'my_plugin_enqueue' ); // CORRECT
|
|
799
|
+
|
|
800
|
+
// Add indexes
|
|
801
|
+
$sql = "CREATE TABLE $table (
|
|
802
|
+
id bigint(20) NOT NULL AUTO_INCREMENT,
|
|
803
|
+
user_id bigint(20) NOT NULL,
|
|
804
|
+
PRIMARY KEY (id),
|
|
805
|
+
KEY user_id (user_id)
|
|
806
|
+
)"; // CORRECT
|
|
807
|
+
|
|
808
|
+
// Use appropriate cache duration
|
|
809
|
+
set_transient( 'key', $data, HOUR_IN_SECONDS ); // CORRECT
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+
---
|
|
813
|
+
|
|
814
|
+
## Performance Checklist
|
|
815
|
+
|
|
816
|
+
### Plugin Development
|
|
817
|
+
|
|
818
|
+
- [ ] Cache expensive queries with transients
|
|
819
|
+
- [ ] Use object caching for frequently accessed data
|
|
820
|
+
- [ ] Optimize database queries (indexes, LIMIT, fields)
|
|
821
|
+
- [ ] Avoid N+1 queries
|
|
822
|
+
- [ ] Load scripts conditionally
|
|
823
|
+
- [ ] Minify CSS and JavaScript
|
|
824
|
+
- [ ] Use defer/async for non-critical scripts
|
|
825
|
+
- [ ] Don't autoload large options
|
|
826
|
+
- [ ] Use lazy loading where appropriate
|
|
827
|
+
- [ ] Monitor and profile performance
|
|
828
|
+
|
|
829
|
+
### Database
|
|
830
|
+
|
|
831
|
+
- [ ] Add indexes to frequently queried columns
|
|
832
|
+
- [ ] Use LIMIT in all queries
|
|
833
|
+
- [ ] Use prepared statements
|
|
834
|
+
- [ ] Avoid complex meta queries
|
|
835
|
+
- [ ] Use taxonomies instead of meta for filtering
|
|
836
|
+
- [ ] Clear transients on data updates
|
|
837
|
+
|
|
838
|
+
### Assets
|
|
839
|
+
|
|
840
|
+
- [ ] Enqueue scripts only where needed
|
|
841
|
+
- [ ] Load scripts in footer
|
|
842
|
+
- [ ] Minify files in production
|
|
843
|
+
- [ ] Combine files when possible
|
|
844
|
+
- [ ] Use CDN for static assets
|
|
845
|
+
- [ ] Optimize images
|
|
846
|
+
|
|
847
|
+
---
|
|
848
|
+
|
|
849
|
+
## Summary
|
|
850
|
+
|
|
851
|
+
**Key Takeaways:**
|
|
852
|
+
|
|
853
|
+
1. **Transient caching**: Cache expensive queries and API calls
|
|
854
|
+
2. **Object caching**: Use `wp_cache_*` functions for frequently accessed data
|
|
855
|
+
3. **Query optimization**: Avoid N+1 queries, use indexes, limit results
|
|
856
|
+
4. **Asset optimization**: Load conditionally, minify, defer/async
|
|
857
|
+
5. **Lazy loading**: Load components and images only when needed
|
|
858
|
+
6. **Autoloaded options**: Don't autoload large data
|
|
859
|
+
7. **Monitoring**: Use Query Monitor and debug tools
|
|
860
|
+
8. **Clear cache**: Always clear cache when data changes
|
|
861
|
+
|
|
862
|
+
**Common Mistakes to Avoid:**
|
|
863
|
+
|
|
864
|
+
- Querying in loops (N+1 problem)
|
|
865
|
+
- Fetching all posts without pagination
|
|
866
|
+
- Autoloading large options
|
|
867
|
+
- Loading scripts on every page
|
|
868
|
+
- Not caching expensive operations
|
|
869
|
+
- Missing database indexes
|
|
870
|
+
- Ignoring slow queries
|
|
871
|
+
|
|
872
|
+
**Resources:**
|
|
873
|
+
|
|
874
|
+
- [WordPress Performance](https://developer.wordpress.org/advanced-administration/performance/)
|
|
875
|
+
- [Transient API](https://developer.wordpress.org/apis/transients/)
|
|
876
|
+
- [Object Cache](https://developer.wordpress.org/reference/classes/wp_object_cache/)
|
|
877
|
+
- [Query Monitor Plugin](https://wordpress.org/plugins/query-monitor/)
|
|
878
|
+
|