PluginProbe
JCH Optimize / trunk
JCH Optimize vtrunk
6.0.2 6.0.1 trunk 1.0.1 1.0.2 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.2.0 1.2.1 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.1.0 2.10.0 2.2.0 2.2.1 All 58 releases
jch-optimize / src / Plugin / Loader.php

Loader.php in JCH Optimize trunk, at src/Plugin/Loader.php

372 lines 12.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * JCH Optimize - Performs several front-end optimizations for fast downloads
5 *
6 * @package jchoptimize/wordpress-platform
7 * @author Samuel Marshall <samuel@jch-optimize.net>
8 * @copyright Copyright (c) 2020 Samuel Marshall / JCH Optimize
9 * @license GNU/GPLv3, or later. See LICENSE file
10 *
11 * If LICENSE file missing, see <http://www.gnu.org/licenses/>.
12 */
13
14 namespace JchOptimize\WordPress\Plugin;
15
16 use _JchOptimizeVendor\V91\Joomla\DI\ContainerAwareInterface;
17 use _JchOptimizeVendor\V91\Joomla\DI\ContainerAwareTrait;
18 use _JchOptimizeVendor\V91\Joomla\Input\Input;
19 use _JchOptimizeVendor\V91\Laminas\Cache\Exception\ExceptionInterface;
20 use _JchOptimizeVendor\V91\Psr\Log\LoggerAwareInterface;
21 use _JchOptimizeVendor\V91\Psr\Log\LoggerAwareTrait;
22 use _JchOptimizeVendor\V91\Psr\Log\LogLevel;
23 use Exception;
24 use JchOptimize\Core\Helper;
25 use JchOptimize\Core\Optimize;
26 use JchOptimize\Core\PageCache\CaptureCache;
27 use JchOptimize\Core\PageCache\PageCache;
28 use JchOptimize\Core\Platform\UtilityInterface;
29 use JchOptimize\Core\Registry;
30 use JchOptimize\Core\Settings;
31 use JchOptimize\Core\SystemUri\SystemUri;
32 use JchOptimize\WordPress\Container\ContainerFactory;
33 use JchOptimize\WordPress\Model\ReCache;
34 use WP_Admin_Bar;
35
36 use function add_action;
37 use function add_filter;
38 use function apply_filters;
39 use function current_user_can;
40 use function defined;
41 use function delete_option;
42 use function do_action;
43 use function gmdate;
44 use function get_option;
45 use function http_response_code;
46 use function is_admin;
47 use function load_plugin_textdomain;
48 use function max;
49 use function plugin_basename;
50 use function register_activation_hook;
51 use function register_uninstall_hook;
52 use function time;
53 use function update_option;
54 use function wp_is_mobile;
55
56 use const JCH_PRO;
57 use const SHORTINIT;
58 use const WP_USE_THEMES;
59 use const WPMU_PLUGIN_DIR;
60
61 class Loader implements LoggerAwareInterface, ContainerAwareInterface
62 {
63 use LoggerAwareTrait;
64 use ContainerAwareTrait;
65
66 public function __construct(
67 private Registry $params,
68 private Admin $admin,
69 private Installer $installer,
70 private PageCache $pageCache,
71 private UtilityInterface $utility,
72 private ?Updater $updater = null,
73 ) {
74 }
75
76 /**
77 * Function register_uninstall_hook can only be used with a static method
78 *
79 * @return void
80 */
81 public static function runUninstallRoutines(): void
82 {
83 /** @var Installer $installer */
84 $installer = ContainerFactory::create()->get(Installer::class);
85 $installer->deactivate();
86 }
87
88 public function preboot_init(): void
89 {
90 if (false !== ($settings = get_option('jch_options'))) {
91 update_option('jch-optimize_settings', $settings);
92 delete_option('jch_options');
93 }
94 }
95
96 public function init(): void
97 {
98 $this->runActivationRoutines();
99
100 if (is_admin()) {
101 if (defined('DOING_AJAX')) { //Ajax functions
102 add_action('wp_ajax_filetree', [$this->admin, 'doAjaxFileTree']);
103 add_action('wp_ajax_multiselect', [$this->admin, 'doAjaxMultiSelect']);
104 add_action('wp_ajax_optimizeimages', [$this->admin, 'doAjaxOptimizeImages']);
105 add_action('wp_ajax_configuresettings', [$this->admin, 'doAjaxConfigureSettings']);
106 add_action('wp_ajax_getcacheinfo', [$this->admin, 'doAjaxGetCacheInfo']);
107 add_action('wp_ajax_onclickicon', [$this->admin, 'doAjaxOnClickIcon']);
108 add_action('wp_ajax_jch_configure_js_table_body', [$this->admin, 'doAjaxJchConfigureJsTableBody']);
109 add_action('wp_ajax_jch_configure_js_auto_save', [$this->admin, 'doAjaxJchConfigureJsAutoSave']);
110 add_action('wp_ajax_jch_cf_verify', [$this->admin, 'doAjaxJchCfVerify']);
111 } else {
112 add_action('admin_menu', [$this->admin, 'addAdminMenu']);
113 add_action('admin_menu', [$this->admin, 'addPageCacheMenu']);
114 add_action('admin_init', [$this->admin, 'registerOptions']);
115 add_filter('plugin_action_links', [$this->admin, 'loadActionLinks'], 10, 2);
116 $this->installer->updateSettings();
117 $this->installer->fixMetaFileSecurity();
118 }
119 } else {
120 $url_exclude = (array)$this->params->get('menuexcludedurl', []);
121 /** @var Input $input */
122 $input = $this->container->get(Input::class);
123 $jch_backend = (string)$input->get('jchbackend');
124 /** @var string|null $nooptimize */
125 $nooptimize = $input->get('jchnooptimize');
126
127 if (
128 defined('WP_USE_THEMES')
129 && WP_USE_THEMES
130 && $jch_backend != '1'
131 && is_null($nooptimize)
132 && version_compare(PHP_VERSION, '7.4.0', '>=')
133 && !defined('DOING_AJAX')
134 && !defined('DOING_CRON')
135 && !defined('APP_REQUEST')
136 && !defined('XMLRPC_REQUEST')
137 && (!defined('SHORTINIT') || (defined('SHORTINIT') && !SHORTINIT))
138 && !Helper::findExcludes($url_exclude, SystemUri::toString())
139 && $input->server->getString('HTTP_SEC_FETCH_DEST') != 'iframe'
140 ) {
141 //Disable NextGen Resource Manager; incompatible with plugin
142 //add_filter( 'run_ngg_resource_manager', '__return_false' );
143 add_action('init', [$this, 'initializeCache'], 0);
144
145 ob_start([$this, 'runOptimize']);
146
147 if (JCH_DEBUG && !$this->params->get('disable_logged_in_users', '1')) {
148 add_action('admin_bar_menu', [$this, 'addMenuToAdminBar'], 101);
149 }
150 }
151 }
152
153 add_action('plugins_loaded', [$this, 'loadPluginTextDomain']);
154 //Function register_uninstall_hook can only be used with a static class method
155 register_uninstall_hook(JCH_PLUGIN_FILE, [__CLASS__, 'runUninstallRoutines']);
156
157 if ($this->params->get('order_plugin', '1')) {
158 add_action('activated_plugin', [$this, 'orderPlugin']);
159 add_action('deactivated_plugin', [$this, 'orderPlugin']);
160 }
161
162 if (JCH_PRO) {
163 $this->loadProUpdater();
164
165 if ($this->params->get('pro_cache_platform', '0')) {
166 add_filter('jch_optimize_get_page_cache_id', [$this, 'getPageCacheHash'], 10, 2);
167 }
168
169 $reCacheModel = $this->getContainer()->get(ReCache::class);
170 /** @see ReCache::reCache() */
171 add_action(ReCache::RECACHE_CRON_HOOK, [$reCacheModel, 'reCache']);
172 add_action('update_option_jch-optimize_settings', [$this, 'respondToOptionUpdates']);
173 }
174 }
175
176 public function runActivationRoutines(): void
177 {
178 //Handles activation routines
179 register_activation_hook(JCH_PLUGIN_FILE, [$this->installer, 'activate']);
180
181 $mu_folder = ABSPATH . 'wp-content/mu-plugins';
182
183 if (defined('WPMU_PLUGIN_DIR') && WPMU_PLUGIN_DIR) {
184 $mu_folder = WPMU_PLUGIN_DIR;
185 }
186
187 if (
188 !file_exists(JCH_PLUGIN_DIR . 'dir.php')
189 || (JCH_PRO && is_admin()
190 && !file_exists($mu_folder . '/jch-optimize-mode-switcher.php'))
191 ) {
192 $this->installer->activate();
193 }
194 }
195
196 public function respondToOptionUpdates($old): void
197 {
198 $new = get_option('jch-optimize_settings');
199 $container = $this->getContainer();
200
201 if (JCH_PRO) {
202 if ($this->params->get('recache_frequency') != '0') {
203 /** @see ReCache::reSchedule() */
204 $container->get(ReCache::class)->reSchedule();
205 }
206
207 $pageCacheSettings = [
208 'cache_enable',
209 Settings::CACHE_PLATFORM,
210 Settings::CAPTURE_CACHE_ENABLE,
211 Settings::PAGE_CACHE_BROWSER_CACHING,
212 Settings::PAGE_CACHE_LIFETIME
213 ];
214
215 $updateHtaccess = false;
216
217 foreach ($pageCacheSettings as $pageCacheSetting) {
218 if ($old[$pageCacheSetting] != $new[$pageCacheSetting]) {
219 $this->params->set($pageCacheSetting, $new[$pageCacheSetting]);
220 $updateHtaccess = true;
221 }
222 }
223
224 if ($updateHtaccess) {
225 $container->get(CaptureCache::class)->updateHtaccess();
226 }
227 }
228 }
229
230 protected function loadProUpdater(): void
231 {
232 $this->updater->load();
233 }
234
235 /**
236 * @throws ExceptionInterface
237 */
238 public function initializeCache(): void
239 {
240 $this->pageCache->initialize();
241 $this->pageCache->outputPageCache();
242 }
243
244 public function runOptimize(string $html): string
245 {
246 if (!Helper::validateHtml($html)) {
247 return $html;
248 }
249
250 try {
251 $this->pageCache->initialize();
252
253 $disableBrowserCaching = $this->pageCache->shouldDisableBrowserCaching();
254
255 if ($disableBrowserCaching) {
256 $this->setNoCacheHeaders();
257 }
258
259 $disableOptimizing = apply_filters('jch_optimize_no_optimize', false);
260 $disableLoggedInUsers = (bool)$this->params->get('disable_logged_in_users', '1');
261
262 //Need to call Utility::isGuest after init has been called
263 if ($disableOptimizing || ($disableLoggedInUsers && !$this->utility->isGuest())) {
264 return $html;
265 }
266
267 gc_collect_cycles();
268 $container = ContainerFactory::create();
269 $optimize = $container->get(Optimize::class);
270
271 if (!$disableBrowserCaching && $this->pageCache->getCachingEnabled()) {
272 if ($this->params->isEnabled(Settings::PAGE_CACHE_BROWSER_CACHING)) {
273 $this->setBrowserCachingHeaders();
274 } else {
275 $this->setNoCacheHeaders();
276 }
277 }
278
279 $optimizedHtml = $optimize->process($html);
280
281 //required for compatibility with Hide My WP Ghost
282 /** @see https://wordpress.org/support/topic/compatibility-with-hide-my-wp-ghost/ */
283 /** @var string $optimizedHtml */
284 $optimizedHtml = apply_filters('jch_optimize_save_content', $optimizedHtml);
285 do_action('jch_optimize_send_headers');
286
287 if (http_response_code() === 200) {
288 $this->pageCache->store($optimizedHtml);
289 }
290 } catch (Exception $e) {
291 $this->logger->log(LogLevel::ERROR, (string)$e);
292
293 $optimizedHtml = $html;
294 }
295
296 return $optimizedHtml;
297 }
298
299 private function setBrowserCachingHeaders(): void
300 {
301 $lifetime = max(0, (int)$this->params->get(Settings::PAGE_CACHE_LIFETIME, '900'));
302 header('Cache-Control: public, max-age=' . $lifetime, true);
303 header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $lifetime) . ' GMT', true);
304 }
305
306 private function setNoCacheHeaders(): void
307 {
308 header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0', true);
309 header('Pragma: no-cache', true);
310 header('Expires: 0', true);
311 }
312
313 public function loadPluginTextDomain(): void
314 {
315 load_plugin_textdomain('jch-optimize', false, dirname(plugin_basename(JCH_PLUGIN_FILE)) . '/languages');
316 }
317
318 public function orderPlugin(): bool
319 {
320 $active_plugins = (array)get_option('active_plugins', []);
321 $order = [
322 'jch-optimize/jch-optimize.php',
323 ];
324
325 //Get the plugins in $order that are currently activated
326 $order_short_list = array_intersect($order, $active_plugins);
327 //Remove plugins in $order_short_list from list of activated plugins
328 $active_plugins_slist = array_diff($active_plugins, $order_short_list);
329 //Merge $order with $active_plugins_list
330 $ordered_active_plugins = array_merge($order_short_list, $active_plugins_slist);
331
332 update_option('active_plugins', $ordered_active_plugins);
333
334 return true;
335 }
336
337 public function getPageCacheHash(array $parts): array
338 {
339 if (wp_is_mobile()) {
340 $parts[] = '_MOBILE_';
341 }
342
343 return $parts;
344 }
345
346 public function addMenuToAdminBar(WP_Admin_Bar $admin_bar): void
347 {
348 if (!current_user_can('manage_options')) {
349 return;
350 }
351
352 $nodes = $admin_bar->get_nodes();
353
354 if (!isset($nodes['jch-optimize-parent'])) {
355 $aArgs = [
356 'id' => 'jch-optimize-parent',
357 'title' => __('JCH Optimize', 'jch-optimize')
358 ];
359
360 $admin_bar->add_node($aArgs);
361 }
362
363 $aArgs = [
364 'parent' => 'jch-optimize-parent',
365 'id' => 'jch-optimize-profiler',
366 'title' => __('Profiler', 'jch-optimize'),
367 ];
368
369 $admin_bar->add_node($aArgs);
370 }
371 }
372