PluginProbe
ezCache / 2.5.2
ezCache v2.5.2
2.6.5 2.6.4 2.6.2 2.6.3 2.6.1 2.6.0 2.5.6 2.5.5 2.5.4 2.5.3 2.5.2 2.5.1 2.5 2.2.1 2.2.2 trunk 1.2 1.2.1 1.2.2 1.2.3 1.2.4 1.3 1.3.1 1.3.10 1.3.11 All 49 releases
ezcache / includes / RedisObjectCache.php

RedisObjectCache.php in ezCache 2.5.2, at includes/RedisObjectCache.php

255 lines 9.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Redis Object Cache Manager
4 * @package Upress\EzCache
5 */
6 namespace Upress\EzCache;
7
8 class RedisObjectCache {
9 const HOST = '127.0.0.1';
10 const PORT = 6379;
11 const TIMEOUT = 1;
12
13 private static $dropin_path;
14 private static $template_path;
15
16 public static function init() {
17 self::$dropin_path = WP_CONTENT_DIR . '/object-cache.php';
18 self::$template_path = EZCACHE_DIR . '/includes/object-cache-dropin.php';
19 $settings = Settings::get_settings();
20 if ( ! empty( $settings->enable_redis_object_cache ) ) {
21 self::maybe_deploy_dropin();
22 }
23 }
24
25 public static function is_available() {
26 $fp = @fsockopen( self::HOST, self::PORT, $errno, $errstr, self::TIMEOUT );
27 if ( $fp ) { fclose( $fp ); return true; }
28 return false;
29 }
30
31 public static function maybe_deploy_dropin() {
32 if ( ! isset( self::$dropin_path ) ) {
33 self::$dropin_path = WP_CONTENT_DIR . '/object-cache.php';
34 self::$template_path = EZCACHE_DIR . '/includes/object-cache-dropin.php';
35 }
36 if ( file_exists( self::$dropin_path ) && ! self::is_our_dropin() ) {
37 return false;
38 }
39 if ( ! file_exists( self::$template_path ) ) {
40 self::write_dropin_file( self::$template_path );
41 }
42 return copy( self::$template_path, self::$dropin_path );
43 }
44
45 public static function remove_dropin() {
46 if ( ! isset( self::$dropin_path ) ) {
47 self::$dropin_path = WP_CONTENT_DIR . '/object-cache.php';
48 }
49 if ( file_exists( self::$dropin_path ) && self::is_our_dropin() ) {
50 return unlink( self::$dropin_path );
51 }
52 return false;
53 }
54
55 public static function is_our_dropin() {
56 if ( ! isset( self::$dropin_path ) ) {
57 self::$dropin_path = WP_CONTENT_DIR . '/object-cache.php';
58 }
59 if ( ! file_exists( self::$dropin_path ) ) { return false; }
60 $header = file_get_contents( self::$dropin_path, false, null, 0, 256 );
61 return strpos( $header, 'ezCache Redis Object Cache Drop-in' ) !== false;
62 }
63
64 public static function get_status() {
65 $settings = Settings::get_settings();
66 $enabled = ! empty( $settings->enable_redis_object_cache );
67 $available = self::is_available();
68 $connected = false;
69 $hit_rate = 0;
70 $memory = '--';
71 $keys = 0;
72 $dropin_active = file_exists( WP_CONTENT_DIR . '/object-cache.php' ) && self::is_our_dropin();
73
74 if ( $enabled && $available ) {
75 $connected = true;
76 $info = self::redis_info();
77 if ( $info ) {
78 $hits = isset( $info['keyspace_hits'] ) ? (int) $info['keyspace_hits'] : 0;
79 $misses = isset( $info['keyspace_misses'] ) ? (int) $info['keyspace_misses'] : 0;
80 $total = $hits + $misses;
81 $hit_rate = $total > 0 ? round( ( $hits / $total ) * 100, 1 ) : 0;
82 $memory = isset( $info['used_memory_human'] ) ? $info['used_memory_human'] : '--';
83 $keys = self::count_keys();
84 }
85 }
86
87 return [
88 'enabled' => $enabled,
89 'available' => $available,
90 'connected' => $connected,
91 'dropin_active' => $dropin_active,
92 'our_dropin' => self::is_our_dropin(),
93 'hit_rate' => $hit_rate,
94 'memory' => $memory,
95 'keys' => $keys,
96 ];
97 }
98
99 public static function flush() {
100 $redis = self::get_connection();
101 if ( ! $redis ) { return false; }
102 try {
103 $keys = $redis->keys( 'ezcache:*' );
104 if ( $keys ) { $redis->del( $keys ); }
105 return true;
106 } catch ( \Exception $e ) { return false; }
107 }
108
109 public static function get_page( $url ) {
110 $redis = self::get_connection();
111 if ( ! $redis ) { return false; }
112 try {
113 $data = $redis->get( 'ezcache:page:' . (defined('DB_NAME') ? DB_NAME . ':' : '') . md5( $url ) );
114 return $data !== false ? $data : false;
115 } catch ( \Exception $e ) { return false; }
116 }
117
118 public static function set_page( $url, $html, $ttl = 604800 ) {
119 $redis = self::get_connection();
120 if ( ! $redis ) { return false; }
121 try {
122 return (bool) $redis->setex( 'ezcache:page:' . (defined('DB_NAME') ? DB_NAME . ':' : '') . md5( $url ), $ttl, $html );
123 } catch ( \Exception $e ) { return false; }
124 }
125
126 public static function delete_page( $url ) {
127 $redis = self::get_connection();
128 if ( ! $redis ) { return false; }
129 try {
130 return (bool) $redis->del( [ 'ezcache:page:' . (defined('DB_NAME') ? DB_NAME . ':' : '') . md5( $url ) ] );
131 } catch ( \Exception $e ) { return false; }
132 }
133
134 private static function get_connection() {
135 static $conn = null;
136 if ( $conn !== null ) { return $conn; }
137 if ( class_exists( 'Redis' ) ) {
138 try {
139 $r = new \Redis();
140 $r->connect( self::HOST, self::PORT, self::TIMEOUT );
141 $conn = $r;
142 return $conn;
143 } catch ( \Exception $e ) { return false; }
144 }
145 $conn = new RedisFallbackSocket( self::HOST, self::PORT, self::TIMEOUT );
146 if ( ! $conn->connected() ) { $conn = false; }
147 return $conn;
148 }
149
150 private static function redis_info() {
151 $redis = self::get_connection();
152 if ( ! $redis ) { return false; }
153 try {
154 if ( class_exists( 'Redis' ) && $redis instanceof \Redis ) { return $redis->info(); }
155 return $redis->info();
156 } catch ( \Exception $e ) { return false; }
157 }
158
159 private static function count_keys() {
160 $redis = self::get_connection();
161 if ( ! $redis ) { return 0; }
162 try {
163 $keys = $redis->keys( 'ezcache:*' );
164 return is_array( $keys ) ? count( $keys ) : 0;
165 } catch ( \Exception $e ) { return 0; }
166 }
167
168 private static function write_dropin_file( $path ) {
169 $content = self::dropin_source();
170 @file_put_contents( $path, $content );
171 }
172
173 private static function dropin_source() {
174 // Return the object-cache drop-in source (stored separately)
175 $src_path = EZCACHE_DIR . '/includes/object-cache-dropin.php';
176 if ( file_exists( $src_path ) ) {
177 return file_get_contents( $src_path );
178 }
179 return '';
180 }
181 }
182
183 /**
184 * Raw-socket Redis client for environments without the php-redis extension.
185 */
186 class RedisFallbackSocket {
187 private $socket = null;
188 private $host;
189 private $port;
190 private $timeout;
191
192 public function __construct( $host, $port, $timeout ) {
193 $this->host = $host;
194 $this->port = $port;
195 $this->timeout = $timeout;
196 $this->socket = @fsockopen( $host, $port, $errno, $errstr, $timeout );
197 if ( $this->socket ) {
198 stream_set_timeout( $this->socket, $timeout );
199 }
200 }
201
202 public function connected() { return (bool) $this->socket; }
203
204 private function send( ...$args ) {
205 if ( ! $this->socket ) { return false; }
206 $cmd = '*' . count( $args ) . "\r\n";
207 foreach ( $args as $a ) { $cmd .= '$' . strlen( $a ) . "\r\n" . $a . "\r\n"; }
208 fwrite( $this->socket, $cmd );
209 return $this->read_response();
210 }
211
212 private function read_response() {
213 $line = fgets( $this->socket );
214 if ( $line === false ) { return false; }
215 $type = $line[0];
216 $data = rtrim( substr( $line, 1 ) );
217 switch ( $type ) {
218 case '+': return $data;
219 case '-': return false;
220 case ':': return (int) $data;
221 case '$':
222 $len = (int) $data;
223 if ( $len === -1 ) { return false; }
224 $bulk = '';
225 while ( strlen( $bulk ) < $len + 2 ) { $bulk .= fread( $this->socket, $len + 2 - strlen( $bulk ) ); }
226 return rtrim( $bulk, "\r\n" );
227 case '*':
228 $count = (int) $data;
229 if ( $count === -1 ) { return []; }
230 $arr = [];
231 for ( $i = 0; $i < $count; $i++ ) { $arr[] = $this->read_response(); }
232 return $arr;
233 }
234 return false;
235 }
236
237 public function get( $key ) { return $this->send( 'GET', $key ); }
238 public function set( $key, $value ) { return $this->send( 'SET', $key, $value ); }
239 public function setex( $key, $ttl, $value ) { return $this->send( 'SETEX', $key, (string) $ttl, $value ); }
240 public function del( array $keys ) { return $this->send( ...array_merge( [ 'DEL' ], $keys ) ); }
241 public function keys( $pattern ) { $r = $this->send( 'KEYS', $pattern ); return is_array( $r ) ? $r : []; }
242 public function info() {
243 $raw = $this->send( 'INFO' );
244 if ( ! $raw ) { return false; }
245 $info = [];
246 foreach ( explode( "\r\n", $raw ) as $line ) {
247 if ( strpos( $line, ':' ) !== false ) {
248 list( $k, $v ) = explode( ':', $line, 2 );
249 $info[ trim( $k ) ] = trim( $v );
250 }
251 }
252 return $info;
253 }
254 }
255