PluginProbe
Sessions / 2.3.1
Sessions v2.3.1
2.1.0 2.10.0 2.11.0 2.12.0 2.13.0 2.13.1 2.13.2 2.13.3 2.14.0 2.2.0 2.3.0 2.3.1 2.4.0 2.4.1 2.5.0 2.6.0 2.6.1 2.6.2 2.7.0 2.8.0 2.9.0 2.9.1 3.0.0 3.1.0 3.1.1 All 39 releases
sessions / includes / system / class-opcache.php

class-opcache.php in Sessions 2.3.1, at includes/system/class-opcache.php

302 lines 8.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * OPcache handling
4 *
5 * Handles all OPcache operations and detection.
6 *
7 * @package System
8 * @author Pierre Lannoy <https://pierre.lannoy.fr/>.
9 * @since 1.0.0
10 */
11
12 namespace POSessions\System;
13
14
15 use POSessions\System\Option;
16 use POSessions\System\File;
17
18 /**
19 * Define the OPcache functionality.
20 *
21 * Handles all OPcache operations and detection.
22 *
23 * @package System
24 * @author Pierre Lannoy <https://pierre.lannoy.fr/>.
25 * @since 1.0.0
26 */
27 class OPcache {
28
29 /**
30 * The list of status.
31 *
32 * @since 1.0.0
33 * @var array $status Maintains the status list.
34 */
35 public static $status = [ 'disabled', 'enabled', 'cache_full', 'restart_pending', 'restart_in_progress', 'recycle_in_progress', 'warmup', 'reset_warmup' ];
36
37 /**
38 * The list of reset types.
39 *
40 * @since 1.0.0
41 * @var array $status Maintains the status list.
42 */
43 public static $resets = [ 'none', 'oom', 'hash', 'manual' ];
44
45 /**
46 * The list of file not compilable/recompilable.
47 *
48 * @since 1.0.0
49 * @var array $status Maintains the file list.
50 */
51 public static $do_not_compile = [ 'includes/plugin.php', 'includes/options.php', 'includes/misc.php', 'includes/menu.php' ];
52
53 /**
54 * Initializes the class and set its properties.
55 *
56 * @since 1.0.0
57 */
58 public function __construct() {
59 }
60
61 /**
62 * Verify if OPcache API usage is restricted.
63 *
64 * @return boolean True if it is restricted, false otherwise.
65 * @since 1.0.0
66 */
67 public static function is_restricted() {
68 // phpcs:ignore
69 set_error_handler( null );
70 // phpcs:ignore
71 $test = @opcache_get_configuration();
72 // phpcs:ignore
73 restore_error_handler();
74 return ! is_array( $test );
75 }
76
77 /**
78 * Get the options infos for Site Health "info" tab.
79 *
80 * @since 1.0.0
81 */
82 public static function debug_info() {
83 $result['product'] = [
84 'label' => 'Product',
85 'value' => self::name(),
86 ];
87 if ( function_exists( 'opcache_get_configuration' ) && function_exists( 'opcache_get_status' ) ) {
88 if ( ! self::is_restricted() ) {
89 // phpcs:ignore
90 $raw = @opcache_get_configuration();
91 if ( array_key_exists( 'directives', $raw ) ) {
92 foreach ( $raw['directives'] as $key => $directive ) {
93 $result[ 'directive_' . $key ] = [
94 'label' => '[Directive] ' . str_replace( 'opcache.', '', $key ),
95 'value' => $directive,
96 ];
97 }
98 }
99 $raw = opcache_get_status();
100 foreach ( $raw as $key => $status ) {
101 if ( 'scripts' === $key ) {
102 continue;
103 }
104 if ( is_array( $status ) ) {
105 foreach ( $status as $skey => $sstatus ) {
106 $result[ 'status_' . $skey ] = [
107 'label' => '[Status] ' . $skey,
108 'value' => $sstatus,
109 ];
110 }
111 } else {
112 $result[ 'status_' . $key ] = [
113 'label' => '[Status] ' . $key,
114 'value' => $status,
115 ];
116 }
117 }
118 } else {
119 $result['product'] = [
120 'label' => 'Status',
121 'value' => 'Unknown - OPcache API usage is restricted',
122 ];
123 }
124 } else {
125 $result['product'] = [
126 'label' => 'Status',
127 'value' => 'Disabled',
128 ];
129 }
130 return $result;
131 }
132
133 /**
134 * Get name and version.
135 *
136 * @return string The name and version of the product.
137 * @since 1.0.0
138 */
139 public static function name() {
140 $result = '';
141 if ( function_exists( 'opcache_get_configuration' ) ) {
142 if ( ! self::is_restricted() ) {
143 // phpcs:ignore
144 $raw = @opcache_get_configuration();
145 if ( array_key_exists( 'version', $raw ) ) {
146 if ( array_key_exists( 'opcache_product_name', $raw['version'] ) ) {
147 $result = $raw['version']['opcache_product_name'];
148 }
149 if ( array_key_exists( 'version', $raw['version'] ) ) {
150 $version = $raw['version']['version'];
151 if ( false !== strpos( $version, '-' ) ) {
152 $version = substr( $version, 0, strpos( $version, '-' ) );
153 }
154 $result .= ' ' . $version;
155 }
156 }
157 } else {
158 $result = '';
159 }
160 }
161 return $result;
162 }
163
164 /**
165 * Invalidate files.
166 *
167 * @param array $files List of files to invalidate.
168 * @param boolean $force Optional. Has the invalidation to be forced.
169 * @return integer The number of invalidated files.
170 * @since 1.0.0
171 */
172 public static function invalidate( $files, $force = false ) {
173 $cpt = 0;
174 if ( function_exists( 'opcache_invalidate' ) && ! self::is_restricted() ) {
175 foreach ( $files as $file ) {
176 if ( 0 === strpos( $file, './' ) ) {
177 $file = str_replace( '..', '', $file );
178 $file = str_replace( './', ABSPATH, $file );
179 if ( opcache_invalidate( $file, $force ) ) {
180 $cpt++;
181 }
182 }
183 }
184 if ( $force ) {
185 $s = 'Forced invalidation';
186 } else {
187 $s = 'Invalidation';
188 }
189 \DecaLog\Engine::eventsLogger( POSE_SLUG )->info( sprintf( '%s: %d file(s).', $s, $cpt ) );
190 }
191 return $cpt;
192 }
193
194 /**
195 * Recompile files.
196 *
197 * @param array $files List of files to recompile.
198 * @param boolean $force Optional. Has the invalidation to be forced.
199 * @return integer The number of recompiled files.
200 * @since 1.0.0
201 */
202 public static function recompile( $files, $force = false ) {
203 $cpt = 0;
204 if ( function_exists( 'opcache_invalidate' ) && function_exists( 'opcache_compile_file' ) && function_exists( 'opcache_is_script_cached' ) && ! self::is_restricted() ) {
205 foreach ( $files as $file ) {
206 if ( 0 === strpos( $file, './' ) ) {
207 foreach ( self::$do_not_compile as $item ) {
208 if ( false !== strpos( $file, $item ) ) {
209 \DecaLog\Engine::eventsLogger( POSE_SLUG )->debug( sprintf( 'File "%s" must not be recompiled.', $file ) );
210 continue 2;
211 }
212 }
213 $file = str_replace( '..', '', $file );
214 $file = str_replace( './', ABSPATH, $file );
215 if ( $force ) {
216 opcache_invalidate( $file, true );
217 }
218 if ( ! opcache_is_script_cached( $file ) ) {
219 try {
220 // phpcs:ignore
221 if ( @opcache_compile_file( $file ) ) {
222 $cpt++;
223 } else {
224 \DecaLog\Engine::eventsLogger( POSE_SLUG )->debug( sprintf( 'Unable to compile file "%s".', $file ) );
225 }
226 } catch ( \Throwable $e ) {
227 \DecaLog\Engine::eventsLogger( POSE_SLUG )->debug( sprintf( 'Unable to compile file "%s": %s.', $file, $e->getMessage() ), [ 'code' => $e->getCode() ] );
228 }
229 } else {
230 \DecaLog\Engine::eventsLogger( POSE_SLUG )->debug( sprintf( 'File "%s" already cached.', $file ) );
231 }
232 }
233 }
234 \DecaLog\Engine::eventsLogger( POSE_SLUG )->info( sprintf( 'Recompilation: %d file(s).', $cpt ) );
235 }
236 return $cpt;
237 }
238
239 /**
240 * Reset the cache (force invalidate all).
241 *
242 * @param boolean $automatic Optional. Is the reset automatically done (via cron, for example).
243 * @since 1.0.0
244 */
245 public static function reset( $automatic = true ) {
246 if ( $automatic && Option::network_get( 'warmup', false ) ) {
247 self::warmup( $automatic, true );
248 } else {
249 $files = [];
250 if ( function_exists( 'opcache_get_status' ) && ! self::is_restricted() ) {
251 try {
252 $raw = opcache_get_status( true );
253 if ( array_key_exists( 'scripts', $raw ) ) {
254 foreach ( $raw['scripts'] as $script ) {
255 if ( false === strpos( $script['full_path'], ABSPATH ) ) {
256 continue;
257 }
258 $files[] = str_replace( ABSPATH, './', $script['full_path'] );
259 }
260 self::invalidate( $files, true );
261 }
262 } catch ( \Throwable $e ) {
263 \DecaLog\Engine::eventsLogger( POSE_SLUG )->error( sprintf( 'Unable to query OPcache status: %s.', $e->getMessage() ), [ 'code' => $e->getCode() ] );
264 }
265 }
266 }
267 }
268
269 /**
270 * Warm-up the site.
271 *
272 * @param boolean $automatic Optional. Is the warmup done (via cron, for example).
273 * @param boolean $force Optional. Has invalidation to be forced.
274 * @return integer The number of recompiled files.
275 * @since 1.0.0
276 */
277 public static function warmup( $automatic = true, $force = false ) {
278 $files = [];
279 foreach ( File::list_files( ABSPATH, 100, [ '/^.*\.php$/i' ], [], true ) as $file ) {
280 $files[] = str_replace( ABSPATH, './', $file );
281 }
282 if ( Environment::is_wordpress_multisite() ) {
283 \DecaLog\Engine::eventsLogger( POSE_SLUG )->info( $automatic ? 'Network reset and warm-up initiated via cron.' : 'Network warm-up initiated via manual action.' );
284 } else {
285 \DecaLog\Engine::eventsLogger( POSE_SLUG )->info( $automatic ? 'Site reset and warm-up initiated via cron.' : 'Site warm-up initiated via manual action.' );
286 }
287 $result = self::recompile( $files, $force );
288 if ( $automatic ) {
289 Cache::set_global( '/Data/ResetWarmupTimestamp', time(), 'check' );
290 } else {
291 Cache::set_global( '/Data/WarmupTimestamp', time(), 'check' );
292 }
293 if ( Environment::is_wordpress_multisite() ) {
294 \DecaLog\Engine::eventsLogger( POSE_SLUG )->info( sprintf( 'Network warm-up terminated. %d files were recompiled', $result ) );
295 } else {
296 \DecaLog\Engine::eventsLogger( POSE_SLUG )->info( sprintf( 'Site warm-up terminated. %d files were recompiled', $result ) );
297 }
298 return $result;
299 }
300
301 }
302