-
Notifications
You must be signed in to change notification settings - Fork 68
/
cli.php
245 lines (223 loc) · 7.16 KB
/
cli.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
<?php
/**
* Various WP Redis CLI utility commands.
*/
/**
* Main WP Redis CLI command class.
*/
class WP_Redis_CLI_Command {
/**
* Launch redis-cli using Redis configuration for WordPress
*/
public function cli() {
global $redis_server;
if ( empty( $redis_server ) ) {
// Attempt to automatically load Pantheon's Redis config from the env.
if ( isset( $_SERVER['CACHE_HOST'] ) && isset( $_SERVER['CACHE_PORT'] ) && isset( $_SERVER['CACHE_PASSWORD'] ) && isset( $_SERVER['CACHE_DB'] ) ) {
$redis_server = [
'host' => sanitize_text_field( $_SERVER['CACHE_HOST'] ),
'port' => sanitize_text_field( $_SERVER['CACHE_PORT'] ),
'auth' => sanitize_text_field( $_SERVER['CACHE_PASSWORD'] ),
'database' => sanitize_text_field( $_SERVER['CACHE_DB'] ),
];
} else {
$redis_server = [
'host' => '127.0.0.1',
'port' => 6379,
'auth' => '',
'database' => 0,
];
}
}
if ( ! isset( $redis_server['database'] ) ) {
$redis_server['database'] = 0;
}
$pipes = null;
$cmd = WP_CLI\Utils\esc_cmd( 'redis-cli -h %s -p %s -a %s -n %s', $redis_server['host'], $redis_server['port'], $redis_server['auth'], $redis_server['database'] );
$process = WP_CLI\Utils\proc_open_compat( $cmd, [ STDIN, STDOUT, STDERR ], $pipes );
$r = proc_close( $process );
exit( (int) $r ); // phpcs:ignore WordPressDotOrg.sniffs.OutputEscaping.UnescapedOutputParameter
}
/**
* Debug object cache hit / miss ratio for any page URL.
*
* ## OPTIONS
*
* [--url=<url>]
* : Execute a request against a specified URL. Defaults to home_url( '/' ).
*
* [--format=<format>]
* : Render the results in a particular format.
*
* @when before_wp_load
*/
public function debug( $_, $assoc_args ) {
global $wp_object_cache;
$this->load_wordpress_with_template();
$data = [
'cache_hits' => $wp_object_cache->cache_hits,
'cache_misses' => $wp_object_cache->cache_misses,
'redis_calls' => $wp_object_cache->redis_calls,
];
WP_CLI::print_value( $data, $assoc_args );
}
/**
* Enable WP Redis by creating the symlink for object-cache.php
*/
public function enable() {
if ( defined( 'WP_REDIS_OBJECT_CACHE' ) && WP_REDIS_OBJECT_CACHE ) {
WP_CLI::success( 'WP Redis is already enabled.' );
return;
}
$drop_in = WP_CONTENT_DIR . '/object-cache.php';
if ( file_exists( $drop_in ) ) {
WP_CLI::error( 'Unknown wp-content/object-cache.php already exists.' );
}
$object_cache = __DIR__ . '/object-cache.php';
$target = self::get_relative_path( $drop_in, $object_cache );
chdir( WP_CONTENT_DIR );
// @codingStandardsIgnoreStart
if ( symlink( $target, 'object-cache.php' ) ) {
// @codingStandardsIgnoreEnd
WP_CLI::success( 'Enabled WP Redis by creating wp-content/object-cache.php symlink.' );
} else {
WP_CLI::error( 'Failed create wp-content/object-cache.php symlink and enable WP Redis.' );
}
}
/**
* Provide details on the Redis connection.
*
* ## OPTIONS
*
* [--reset]
* : Reset Redis stats. Only affects `lifetime_hitrate` currently.
*
* [--field=<field>]
* : Get the value of a particular field.
*
* [--format=<format>]
* : Render results in a particular format.
* ---
* default: table
* options:
* - table
* - json
* - yaml
* ---
*
* ## EXAMPLES
*
* $ wp redis info
* +-------------------+-----------+
* | Field | Value |
* +-------------------+-----------+
* | status | connected |
* | used_memory | 529.25K |
* | uptime | 0 days |
* | key_count | 20 |
* | instantaneous_ops | 9/sec |
* | lifetime_hitrate | 53.42% |
* | redis_host | 127.0.0.1 |
* | redis_port | 6379 |
* | redis_auth | |
* | redis_database | 0 |
* +-------------------+-----------+
*
* $ wp redis info --field=used_memory
* 529.38K
*
* $ wp redis info --reset
* Success: Redis stats reset.
*/
public function info( $_, $assoc_args ) {
global $wp_object_cache;
if ( ! defined( 'WP_REDIS_OBJECT_CACHE' ) || ! WP_REDIS_OBJECT_CACHE ) {
WP_CLI::error( 'WP Redis object-cache.php file is missing from the wp-content/ directory.' );
}
if ( $wp_object_cache->is_redis_connected && WP_CLI\Utils\get_flag_value( $assoc_args, 'reset' ) ) {
// Redis::resetStat() isn't functional, see https://github.com/phpredis/phpredis/issues/928.
if ( $wp_object_cache->redis->eval( "return redis.call('CONFIG','RESETSTAT')" ) ) {
WP_CLI::success( 'Redis stats reset.' );
} else {
WP_CLI::error( "Couldn't reset Redis stats." );
}
} else {
$data = wp_redis_get_info();
if ( is_wp_error( $data ) ) {
WP_CLI::error( $data );
}
$formatter = new \WP_CLI\Formatter( $assoc_args, array_keys( $data ) );
$formatter->display_item( $data );
}
}
/**
* Runs through the entirety of the WP bootstrap process
*/
private function load_wordpress_with_template() {
global $wp_query;
WP_CLI::get_runner()->load_wordpress();
// Set up the main WordPress query.
wp();
$interpreted = [];
foreach ( $wp_query as $key => $value ) {
if ( 0 === stripos( $key, 'is_' ) && $value ) {
$interpreted[] = $key;
}
}
WP_CLI::debug( 'Main WP_Query: ' . implode( ', ', $interpreted ), 'redis-debug' );
define( 'WP_USE_THEMES', true );
add_filter(
'template_include',
function ( $template ) {
$display_template = str_replace( dirname( get_template_directory() ) . '/', '', $template );
WP_CLI::debug( "Theme template: {$display_template}", 'redis-debug' );
return $template;
},
999
);
// Template is normally loaded in global scope, so we need to replicate.
foreach ( $GLOBALS as $key => $value ) {
// phpcs:ignore PHPCompatibility.Variables.ForbiddenGlobalVariableVariable.NonBareVariableFound
global $$key;
}
// Load the theme template.
ob_start();
require_once ABSPATH . WPINC . '/template-loader.php';
ob_get_clean();
}
/**
* Get the relative path between two files
*
* @see http://stackoverflow.com/questions/2637945/getting-relative-path-from-absolute-path-in-php
*/
private static function get_relative_path( $from, $to ) {
// some compatibility fixes for Windows paths.
$from = is_dir( $from ) ? rtrim( $from, '\/' ) . '/' : $from;
$to = is_dir( $to ) ? rtrim( $to, '\/' ) . '/' : $to;
$from = str_replace( '\\', '/', $from );
$to = str_replace( '\\', '/', $to );
$from = explode( '/', $from );
$to = explode( '/', $to );
$rel_path = $to;
foreach ( $from as $depth => $dir ) {
// find first non-matching dir.
if ( $dir === $to[ $depth ] ) {
// ignore this directory.
array_shift( $rel_path );
} else {
// get number of remaining dirs to $from.
$remaining = count( $from ) - $depth;
if ( $remaining > 1 ) {
// add traversals up to first matching dir.
$pad_length = ( count( $rel_path ) + $remaining - 1 ) * -1;
$rel_path = array_pad( $rel_path, $pad_length, '..' );
break;
} else {
$rel_path[0] = './' . $rel_path[0];
}
}
}
return implode( '/', $rel_path );
}
}
WP_CLI::add_command( 'redis', 'WP_Redis_CLI_Command' );