wp_back/wp-content/plugins/wp-asset-clean-up/classes/Plugin.php
2024-05-20 15:37:46 +03:00

447 lines
16 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace WpAssetCleanUp;
use WpAssetCleanUp\OptimiseAssets\OptimizeCommon;
use WpAssetCleanUp\OptimiseAssets\OptimizeCss;
use WpAssetCleanUp\OptimiseAssets\OptimizeJs;
/**
* Class Plugin
* @package WpAssetCleanUp
*/
class Plugin
{
/**
*
*/
const RATE_URL = 'https://wordpress.org/support/plugin/wp-asset-clean-up/reviews/#new-post';
/**
* Plugin constructor.
*/
public function __construct()
{
register_activation_hook(WPACU_PLUGIN_FILE, array($this, 'whenActivated'));
register_deactivation_hook(WPACU_PLUGIN_FILE, array($this, 'whenDeactivated'));
}
/**
*
*/
public function init()
{
// After fist time activation or in specific situations within the Dashboard
add_action('admin_init', array($this, 'adminInit'));
// [wpacu_lite]
// Admin footer text: Ask the user to review the plugin
add_filter('admin_footer_text', array($this, 'adminFooterText'), 1, 1);
// [/wpacu_lite]
// Show default action links: "Getting Started", "Settings"
add_filter('plugin_action_links_'.WPACU_PLUGIN_BASE, array($this, 'addActionLinksInPluginsPage'));
}
// [wpacu_lite]
/**
* @param $text
*
* @return string
*/
public function adminFooterText($text)
{
if (Menu::isPluginPage()) {
$text = sprintf(__('Thank you for using %s', 'wp-asset-clean-up'), WPACU_PLUGIN_TITLE.' v'.WPACU_PLUGIN_VERSION)
. ' <span class="dashicons dashicons-smiley"></span> &nbsp;&nbsp;';
$text .= sprintf(
__('If you like it, please %s<strong>rate</strong> %s%s %s on WordPress.org to help me spread the word to the community.', 'wp-asset-clean-up'),
'<a target="_blank" href="'.self::RATE_URL.'">',
WPACU_PLUGIN_TITLE,
'</a>',
'<a target="_blank" href="'.self::RATE_URL.'"><span class="dashicons dashicons-wpacu dashicons-star-filled"></span><span class="dashicons dashicons-wpacu dashicons-star-filled"></span><span class="dashicons dashicons-wpacu dashicons-star-filled"></span><span class="dashicons dashicons-wpacu dashicons-star-filled"></span><span class="dashicons dashicons-wpacu dashicons-star-filled"></span></a>'
);
}
return $text;
}
// [/wpacu_lite]
/**
* Actions taken when the plugin is activated
*/
public function whenActivated()
{
if (WPACU_WRONG_PHP_VERSION === 'true') {
$recordMsg = __( '"Asset CleanUp" plugin has not been activated because the PHP version used on this server is below 5.6.',
'wp-asset-clean-up' );
deactivate_plugins( WPACU_PLUGIN_BASE );
error_log( $recordMsg );
wp_die($recordMsg);
}
// Prepare for the redirection to the WPACU_ADMIN_PAGE_ID_START plugin page
// If there is no record that the plugin was already activated at least once
if ( ! get_option(WPACU_PLUGIN_ID.'_first_usage') ) {
set_transient(WPACU_PLUGIN_ID . '_redirect_after_activation', 1, 15);
// Make a record when Asset CleanUp (Pro) is used for the first time
// In case this is the first time the plugin is activated
self::triggerFirstUsage();
}
/**
* Note: Could be /wp-content/uploads/ if constant WPACU_CACHE_DIR was used
*
* /wp-content/cache/asset-cleanup/
* /wp-content/cache/asset-cleanup/index.php
* /wp-content/cache/asset-cleanup/.htaccess
*
* /wp-content/cache/asset-cleanup/css/
* /wp-content/cache/asset-cleanup/css/item/
* /wp-content/cache/asset-cleanup/css/index.php
*
* /wp-content/cache/asset-cleanup/js/
* /wp-content/cache/asset-cleanup/js/item/
* /wp-content/cache/asset-cleanup/js/index.php
*
*/
self::createCacheFoldersFiles(array('css','js'));
// Do not apply plugin's settings/rules on WooCommerce/EDD Checkout/Cart pages
if (function_exists('wc_get_page_id')) {
if ($wooCheckOutPageId = wc_get_page_id('checkout')) {
Misc::doNotApplyOptimizationOnPage($wooCheckOutPageId);
}
if ($wooCartPageId = wc_get_page_id('cart')) {
Misc::doNotApplyOptimizationOnPage($wooCartPageId);
}
}
if (function_exists('edd_get_option') && $eddPurchasePage = edd_get_option('purchase_page', '')) {
Misc::doNotApplyOptimizationOnPage($eddPurchasePage);
}
}
/**
* Actions taken when the plugin is deactivated
*/
public function whenDeactivated()
{
// Clear traces of the plugin which are re-generated once the plugin is enabled
// This is good when the admin wants to completely uninstall the plugin
self::clearAllTransients();
self::removeCacheDirWithoutAssets();
// Clear other plugin's cache (if they are active)
OptimizeCommon::clearOtherPluginsCache();
}
/**
* Removes all plugin's transients, this is usually done when the plugin is deactivated
*/
public static function clearAllTransients()
{
global $wpdb;
// Remove all transients
$transientLikes = array(
'_transient_wpacu_',
'_transient_'.WPACU_PLUGIN_ID.'_'
);
$transientLikesSql = '';
foreach ($transientLikes as $transientLike) {
$transientLikesSql .= " option_name LIKE '".$transientLike."%' OR ";
}
$transientLikesSql = rtrim($transientLikesSql, ' OR ');
$sqlQuery = <<<SQL
SELECT option_name FROM `{$wpdb->prefix}options` WHERE {$transientLikesSql}
SQL;
$transientsToClear = $wpdb->get_col($sqlQuery);
if (! empty($transientsToClear)) {
foreach ( $transientsToClear as $transientToClear ) {
$transientNameToClear = str_replace( '_transient_', '', $transientToClear );
delete_transient( $transientNameToClear );
}
}
}
/**
* This is usually triggered when the plugin is deactivated
* If the caching directory doesn't have any CSS/JS left, it will clear itself
* The admin might want to clear all traces of the plugin
* If the plugin is re-activated, the caching directory will be re-created automatically
*/
public static function removeCacheDirWithoutAssets()
{
$pathToCacheDir = WP_CONTENT_DIR . OptimizeCommon::getRelPathPluginCacheDir();
if (! is_dir($pathToCacheDir)) {
return;
}
$pathToCacheDirCss = WP_CONTENT_DIR . OptimizeCss::getRelPathCssCacheDir();
$pathToCacheDirJs = WP_CONTENT_DIR . OptimizeJs::getRelPathJsCacheDir();
$allCssFiles = glob( $pathToCacheDirCss . '**/*.css' );
$allJsFiles = glob( $pathToCacheDirJs . '**/*.js' );
// Only valid when there's no CSS or JS (not one single file) there
if ( count( $allCssFiles ) === 0 && count( $allJsFiles ) === 0 ) {
$dirItems = new \RecursiveDirectoryIterator( $pathToCacheDir );
$allDirs = array($pathToCacheDir);
// First, remove the files
foreach ( new \RecursiveIteratorIterator( $dirItems, \RecursiveIteratorIterator::SELF_FIRST,
\RecursiveIteratorIterator::CATCH_GET_CHILD ) as $item) {
if (is_dir($item)) {
$allDirs[] = $item;
} else {
@unlink($item);
}
}
if ( ! empty($allDirs) ) {
usort( $allDirs, static function( $a, $b ) {
return strlen( $b ) - strlen( $a );
} );
// Then, remove the empty dirs in descending order (up to the root)
foreach ($allDirs as $dir) {
Misc::rmDir($dir);
}
}
}
}
/**
* @param $assetTypes
*/
public static function createCacheFoldersFiles($assetTypes)
{
foreach ($assetTypes as $assetType) {
if ($assetType === 'css') {
$cacheDir = WP_CONTENT_DIR . OptimiseAssets\OptimizeCss::getRelPathCssCacheDir();
} elseif ($assetType === 'js') {
$cacheDir = WP_CONTENT_DIR . OptimiseAssets\OptimizeJs::getRelPathJsCacheDir();
} else {
return;
}
$emptyPhpFileContents = <<<TEXT
<?php
// Silence is golden.
TEXT;
$htAccessContents = <<<HTACCESS
<IfModule mod_autoindex.c>
Options -Indexes
</IfModule>
HTACCESS;
if ( ! is_dir( $cacheDir ) ) {
@mkdir( $cacheDir, FS_CHMOD_DIR, true );
}
if ( ! is_file( $cacheDir . 'index.php' ) ) {
// /wp-content/cache/asset-cleanup/cache/(css|js)/index.php
FileSystem::filePutContents( $cacheDir . 'index.php', $emptyPhpFileContents );
}
if ( ! is_dir( $cacheDir . OptimizeCommon::$optimizedSingleFilesDir ) ) {
// /wp-content/cache/asset-cleanup/cache/(css|js)/item/
@mkdir( $cacheDir . OptimizeCommon::$optimizedSingleFilesDir, FS_CHMOD_DIR );
}
// For large inline STYLE & SCRIPT tags
if ( ! is_dir( $cacheDir . OptimizeCommon::$optimizedSingleFilesDir.'/inline' ) ) {
// /wp-content/cache/asset-cleanup/cache/(css|js)/item/inline/
@mkdir( $cacheDir . OptimizeCommon::$optimizedSingleFilesDir.'/inline', FS_CHMOD_DIR );
}
if ( ! is_file( $cacheDir . OptimizeCommon::$optimizedSingleFilesDir.'/inline/index.php' ) ) {
// /wp-content/cache/asset-cleanup/cache/(css|js)/item/inline/index.php
FileSystem::filePutContents( $cacheDir . OptimizeCommon::$optimizedSingleFilesDir.'/inline/index.php', $emptyPhpFileContents );
}
$htAccessFilePath = dirname( $cacheDir ) . '/.htaccess';
if ( ! is_file( $htAccessFilePath ) ) {
// /wp-content/cache/asset-cleanup/.htaccess
FileSystem::filePutContents( $htAccessFilePath, $htAccessContents );
}
if ( ! is_file( dirname( $cacheDir ) . '/index.php' ) ) {
// /wp-content/cache/asset-cleanup/index.php
FileSystem::filePutContents( dirname( $cacheDir ) . '/index.php', $emptyPhpFileContents );
}
}
// Storage directory for JSON/TEXT files (information purpose)
$storageDir = WP_CONTENT_DIR . OptimiseAssets\OptimizeCommon::getRelPathPluginCacheDir() . '_storage/';
if ( ! is_dir($storageDir . OptimizeCommon::$optimizedSingleFilesDir) ) {
@mkdir( $storageDir . OptimizeCommon::$optimizedSingleFilesDir, FS_CHMOD_DIR, true );
}
$siteStorageCache = $storageDir.'/'.str_replace(array('https://', 'http://', '//'), '', site_url());
if ( ! is_dir($storageDir) ) {
@mkdir( $siteStorageCache, FS_CHMOD_DIR, true );
}
}
/**
*
*/
public function adminInit()
{
if ( // If this condition does not match, do not make the extra DB calls to "options" table to save resources
isset($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], '/plugins.php') !== false &&
get_transient(WPACU_PLUGIN_ID . '_redirect_after_activation') ) {
// Remove it as only one redirect is needed (first time the plugin is activated)
delete_transient(WPACU_PLUGIN_ID . '_redirect_after_activation');
// Do the 'first activation time' redirection
wp_redirect(admin_url('admin.php?page=' . WPACU_ADMIN_PAGE_ID_START));
exit();
}
}
/**
* @param $links
*
* @return mixed
*/
public function addActionLinksInPluginsPage($links)
{
$links['getting_started'] = '<a href="admin.php?page=' . WPACU_PLUGIN_ID . '_getting_started">'.__('Getting Started', 'wp-asset-clean-up').'</a>';
$links['settings'] = '<a href="admin.php?page=' . WPACU_PLUGIN_ID . '_settings">'.__('Settings', 'wp-asset-clean-up').'</a>';
// [wpacu_lite]
$allPlugins = get_plugins();
// If the Pro version is not installed (active or not), show the upgrade link
if (! array_key_exists('wp-asset-clean-up-pro/wpacu.php', $allPlugins)) {
$links['go_pro'] = '<a target="_blank" style="font-weight: bold;" href="'.apply_filters('wpacu_go_pro_affiliate_link', WPACU_PLUGIN_GO_PRO_URL).'">'.__('Go Pro', 'wp-asset-clean-up').'</a>';
}
// [/wpacu_lite]
return $links;
}
/**
* Make a record when Asset CleanUp (Pro) is used for the first time (if it's not there already)
*/
public static function triggerFirstUsage()
{
Misc::addUpdateOption(WPACU_PLUGIN_ID . '_first_usage', time());
}
/**
* This works like /?wpacu_no_load with a fundamental difference:
* It needs to be triggered through a very early 'init' / 'setup_theme' action hook after all plugins are loaded, thus it can't be used in /early-triggers.php
* e.g. in situations when the page is an AMP one, prevent any changes to the HTML source by Asset CleanUp (Pro)
*
* @param string $tagActionName
* @param string $htmlSource
*
* @return bool
*/
public static function preventAnyFrontendOptimization($tagActionName = '', $htmlSource = '')
{
// Only relevant if all the plugins are already loaded
// and in the front-end view
if (! defined('WPACU_ALL_ACTIVE_PLUGINS_LOADED') || is_admin()) {
return false;
}
// Perhaps the editor from "Pro" (theme.co) is on
if (apply_filters('wpacu_prevent_any_frontend_optimization', false)) {
return true;
}
// e.g. /amp/ - /amp? - /amp/? - /?amp or ending in /amp
$isAmpInRequestUri = ( (isset($_SERVER['REQUEST_URI']) && (preg_match('/(\/amp$|\/amp\?)|(\/amp\/|\/amp\/\?)/', $_SERVER['REQUEST_URI'])))
|| isset($_GET['amp']) );
// Is it an AMP endpoint?
if ( ($isAmpInRequestUri && Misc::isPluginActive('accelerated-mobile-pages/accelerated-mobile-pages.php')) // "AMP for WP Accelerated Mobile Pages"
|| ($isAmpInRequestUri && Misc::isPluginActive('amp/amp.php')) // "AMP WordPress plugin"
|| ((function_exists('is_wp_amp') && Misc::isPluginActive('wp-amp/wp-amp.php') && is_wp_amp())) // "WP AMP — Accelerated Mobile Pages for WordPress and WooCommerce" (Premium plugin)
) {
return true; // do not print anything on an AMP page
}
// Some pages are AMP but their URI does not end in /amp
if ( ! defined('WPACU_DO_EXTRA_CHECKS_FOR_AMP') &&
( Misc::isPluginActive('accelerated-mobile-pages/accelerated-mobile-pages.php')
|| Misc::isPluginActive('amp/amp.php')
|| Misc::isPluginActive('wp-amp/wp-amp.php') )
) {
define('WPACU_DO_EXTRA_CHECKS_FOR_AMP', true);
}
if ( isset($_GET['wpacu_clean_load']) ) {
return true;
}
// $tagActionName needs to be different from 'parse_query' because is_singular() would trigger too soon and cause notice errors
// Has the following page option set: "Do not apply any front-end optimization on this page (this includes any changes related to CSS/JS files)"
if ($tagActionName !== 'parse_query' && MetaBoxes::hasNoFrontendOptimizationPageOption()) {
return true;
}
// e.g. it could be JS content loaded dynamically such as /?wpml-app=ate-widget
// in this case, any Asset CleanUp alteration of the content should be avoided
// often, the code below is not reached as extra measures are taken before if well known plugins are used
if ($htmlSource !== '') {
$startsWithPassed = $endsWithPassed = false;
// More common delimiters can be added with time
// This is just an extra measure to prevent possible empty pages due to lots of memory used in case a possible JavaScript output is too large
$startsWithAnyFromTheList = array(
'/*!',
'(function()'
);
$endsWithAnyFromTheList = array(
');',
')'
);
// Possible JS content
foreach ($startsWithAnyFromTheList as $startsWithString) {
if (substr(trim($htmlSource), 0, strlen($startsWithString)) === $startsWithString) {
$startsWithPassed = true;
break;
}
}
if ($startsWithPassed) {
foreach ($endsWithAnyFromTheList as $endsWithString) {
if (substr(trim($htmlSource), -strlen($endsWithString)) === $endsWithString) {
$endsWithPassed = true;
break;
}
}
}
if ($startsWithPassed && $endsWithPassed) {
return true;
}
}
return false;
}
}