Current File : //usr/local/lsws/add-ons/webcachemgr/src/WPCaller.php |
<?php
/** *********************************************
* LiteSpeed Web Server Cache Manager
*
* @author Michael Alegre
* @copyright 2018-2024 LiteSpeed Technologies, Inc.
* *******************************************
*/
namespace Lsc\Wp;
use Lsc\Wp\Context\Context;
use Lsc\Wp\WpWrapper\PluginUpgrader;
use Lsc\Wp\WpWrapper\WpConstants;
use Lsc\Wp\WpWrapper\WpFuncs;
use Lsc\Wp\WpWrapper\Wpdb;
use Lsc\Wp\WpWrapper\WpQuery;
use Lsc\Wp\WpWrapper\WpTextdomainRegistry;
/**
* Calls WP internal functions in SHORTINIT mode.
*/
class WPCaller
{
/**
* @var string
*/
const LSCWP_PLUGIN = 'litespeed-cache/litespeed-cache.php';
/**
* @var string
*/
const LSCWP_HTTP_HOST_TEST = 'litespeed_something_is_wrong';
/**
* @var null|WPCaller
*/
private static $instance = null;
/**
* @var WPInstall
*/
private $currInstall;
/**
* @since 1.13.4.4
* @var string
*/
private $advancedCacheFile;
/**
* @var bool
*/
private $loadLscwp;
/**
* @var string
*/
private $pluginEntry;
/**
* @since 1.13
* @var null|string
*/
private $installedLscwpVer = null;
/**
* @var string[]
*/
private $outputResult = array();
/**
* @var string
*/
private $massIncr = '';
/**
*
* @param WPInstall $curInstall
* @param bool $loadLscwp
*
* @throws LSCMException Thrown indirectly by $this->init() call.
*/
private function __construct( WPInstall $curInstall, $loadLscwp )
{
$this->currInstall = $curInstall;
$this->loadLscwp = $loadLscwp;
$this->init();
}
/**
*
* @since 1.13.4.4
*
* @throws LSCMException Thrown indirectly by $this->initWp() call.
*/
private function init()
{
$this->advancedCacheFile =
"{$this->currInstall->getPath()}/wp-content/advanced-cache.php";
$this->initWp();
}
/**
*
* @param WPInstall $currInstall
* @param bool $loadLscwp
*
* @return WPCaller
*
* @throws LSCMException Thrown indirectly by "new self()" call.
*/
public static function getInstance(
WPInstall $currInstall,
$loadLscwp = true )
{
if ( self::$instance == null
|| self::$instance->currInstall !== $currInstall ) {
self::$instance = new self($currInstall, $loadLscwp);
}
return self::$instance;
}
/**
*
* @param int $errno Not used at this time.
* @param string $errstr
*
* @return bool
*
* @noinspection PhpUnusedParameterInspection
*/
public static function warning_handler( $errno, $errstr )
{
$strs = array(
'ini_set() has been disabled for security reasons',
'Constant FS_METHOD already defined'
);
if ( in_array($errstr, $strs) ) {
/**
* Throw this warning out.
*/
return true;
}
return false;
}
/**
* Deprecated 03/12/19 as this function is no longer used.
*
* Prevents database table options not in the $allowedOptions list
* from having their value updated by others during execution.
*
* Run when WP function apply_filters('pre_update_option', ...) is called
* in WP function update_option().
*
* @deprecated
*
* @param mixed $value New option value.
* @param string $option Option name in db.
* @param mixed $old_value Old option value.
*
* @return mixed
*/
public static function lock_database( $value, $option, $old_value )
{
$allowedOptions = array(
'litespeed-cache-conf',
'_transient_lscwp_whm_install',
'active_plugins',
'_transient_doing_cron',
'_site_transient_update_plugins',
'uninstall_plugins'
);
if ( in_array($option, $allowedOptions) ) {
return $value;
}
else {
return $old_value;
}
}
/**
* Redefine disabled PHP global/core functions that no longer exist
* (PHP 8+).
*
* @since 1.13.10
*/
private static function redefineDisabledFunctions()
{
if ( Util::betterVersionCompare(phpversion(), '8.0', '>=') ) {
include_once(__DIR__ . '/RedefineGlobalFuncs.php');
}
}
/**
*
* @return string[]
*/
public function getOutputResult()
{
return $this->outputResult;
}
/**
* Adds key value pair to $this->outputResult to be grabbed later in
* the $output variable of the UserCommand::issue() exec call.
*
* @param string $key
* @param mixed $value
*/
private function outputResult( $key, $value )
{
$this->outputResult[$key] = $value;
}
/**
*
* @deprecated 1.9 Use Logger::getLogMsgQueue() to get these messages as
* LogEntry objects.
*
* @return string[]
*
* @throws LSCMException Thrown indirectly by Logger::getLogMsgQueue()
* call.
*/
public function getDebugMsgs()
{
$debugMsgs = array();
$msgQueue = Logger::getLogMsgQueue();
foreach ( $msgQueue as $logEntry ) {
$label = Logger::getLvlDescr($logEntry->getLvl());
$debugMsgs[] = "[$label] {$logEntry->getMsg()}";
}
return $debugMsgs;
}
/**
* @deprecated 1.9 Deprecated 07/30/19. Use
* Logger::getUiMsgs(Logger::UI_ERR) to get these messages.
*
* @return string[]
*
* @throws LSCMException Thrown indirectly by Logger::getUiMsgs() call.
*/
public function getErrMsgs()
{
return Logger::getUiMsgs(Logger::UI_ERR);
}
/**
*
* @deprecated 1.9 Deprecated 07/30/19. Use
* Logger::getUiMsgs(Logger::UI_SUCC) to get these messages.
*
* @return string[]
*
* @throws LSCMException Thrown indirectly by Logger::getUiMsgs() call.
*/
public function getSuccMsgs()
{
return Logger::getUiMsgs(Logger::UI_SUCC);
}
/**
*
* WP Variables $table_prefix
*
* @global string $table_prefix
*
* @return string
*/
private function getSiteURL()
{
global $table_prefix;
$siteURL = WpFuncs::getOption('siteurl');
if ( !$siteURL ) {
return '';
}
if ( strpos($siteURL, self::LSCWP_HTTP_HOST_TEST) !== false ) {
/**
* User is setting WP_SITEURL using fake $_SERVER['HTTP_HOST'].
* Get siteurl value from DB directly.
*/
return WpFuncs::getVar(
"SELECT option_value FROM {$table_prefix}options "
. "WHERE option_name = 'siteurl'"
);
}
return $siteURL;
}
/**
*
* @since 1.17.0.3
*
* @param string $advCacheFileContents
*
* @return bool
*/
private static function advancedCacheFileBelongsToLscwp(
$advCacheFileContents
)
{
if ( self::advancedCacheFileHasLscacheDefine($advCacheFileContents) ) {
return true;
}
if ( self::advancedCacheFileHasPlaceholderMsg($advCacheFileContents) ) {
return true;
}
return false;
}
/**
*
* @since 1.13.4.3
*
* @return bool
*/
private function generic3rdPartyAdvCachePluginExists()
{
if ( Util::betterVersionCompare($this->installedLscwpVer, '3.0.4', '>=') ) {
if ( file_exists($this->advancedCacheFile) ) {
$advCacheFileContents =
file_get_contents($this->advancedCacheFile);
if ( $advCacheFileContents !== '' ) {
/**
* Old LSCWP advanced-cache.php file is no longer used in
* these versions but is also never cleaned up. As a result,
* any existing advanced-cache.php files need to have their
* contents checked to avoid detecting an old LSCWP
* advanced-cache.php file as a generic 3rd-party
* advanced-cache plugin.
*/
return !self::advancedCacheFileBelongsToLscwp(
$advCacheFileContents
);
}
}
}
elseif ( !defined('LSCACHE_ADV_CACHE')
|| WpConstants::getWpConstant('LSCACHE_ADV_CACHE') !== true ) {
return true;
}
return false;
}
/**
*
* @since 1.13.8 Added optional parameter $output.
*
* @param WPInstall $install
* @param bool $output
*
* @return int
*
* @throws LSCMException Thrown indirectly by Logger::uiError() call.
* @throws LSCMException Thrown indirectly by $install->addUserFlagFile()
* call.
* @throws LSCMException Thrown indirectly by $install->addUserFlagFile()
* call.
* @throws LSCMException Thrown indirectly by Logger::notice() call.
* @throws LSCMException Thrown indirectly by Logger::notice() call.
*/
private function checkStatus( WPInstall $install, $output = false )
{
if ( $output ) {
if ( ($siteUrl = $this->getSiteURL()) === '' ) {
Logger::uiError(
'Could not retrieve siteURL to match against known '
. 'docroots.'
);
$install->addUserFlagFile();
return (WPInstall::ST_ERR_SITEURL | WPInstall::ST_FLAGGED);
}
$this->outputResult('SITEURL', $siteUrl);
}
$status = 0;
/**
* Check if plugin files exist first, as status in db could be stale if
* LSCWP was removed manually.
*/
if ( file_exists($this->pluginEntry)
&& WpFuncs::isPluginActive(self::LSCWP_PLUGIN) ) {
$status |= WPInstall::ST_PLUGIN_ACTIVE;
//TODO: Get rid of ST_LSC_ADVCACHE_DEFINED status or replace with
// new "is caching enabled" define check.
if ( $this->generic3rdPartyAdvCachePluginExists() ) {
$status |= WPInstall::ST_FLAGGED;
}
else {
$status |= WPInstall::ST_LSC_ADVCACHE_DEFINED;
}
}
else {
$status |= WPInstall::ST_PLUGIN_INACTIVE;
}
if ( $install->hasFlagFile() ) {
$status |= WPInstall::ST_FLAGGED;
}
if ( $status & WPInstall::ST_FLAGGED ) {
if ( $install->addUserFlagFile() ) {
Logger::notice('Install is flagged');
}
else {
Logger::notice('Install is not flagged');
}
}
return $status;
}
/**
*
* @param bool $output
*
* @return int
*
* @throws LSCMException Thrown indirectly by $this->checkStatus() call.
*/
public function updateStatus( $output = false )
{
$status = $this->checkStatus($this->currInstall, $output);
$this->currInstall->setStatus($status);
if ( $output ) {
$this->outputResult('STATUS', $status);
}
if ( $status & WPInstall::ST_ERR_SITEURL ) {
return UserCommand::EXIT_FAIL;
}
return UserCommand::EXIT_SUCC;
}
/**
*
* @return int
*
* @throws LSCMException Thrown indirectly by $this->updateStatus() call.
*/
private function getCurrStatus()
{
$this->updateStatus();
return $this->currInstall->getStatus();
}
/**
* Check if any known cache plugins that do not use an advanced-cache.php
* file are active for this WordPress installation. If any of these plugins
* are found, that plugin's slug is returned.
*
* @since 1.9.1
*
* @return string Empty string or detected active cache plugin slug.
*/
private function checkForKnownNonAdvCachePlugins()
{
$knownPlugins = array(
'wp-fastest-cache' => 'wp-fastest-cache/wpFastestCache.php'
);
foreach ( $knownPlugins as $slug => $plugin ) {
/**
* Check if plugin files exist first, as status in db could be
* stale if plugin files were removed manually.
*/
$pluginExistsAndIsActive = (
file_exists(
WpConstants::getWpConstant('WP_PLUGIN_DIR') . "/$plugin"
)
&&
WpFuncs::isPluginActive($plugin)
);
if ( $pluginExistsAndIsActive) {
return $slug;
}
}
return '';
}
/**
*
* @param bool $isMassAction
* @param bool $isNewInstall
*
* @return bool
*
* @throws LSCMException Thrown indirectly by $this->getCurrStatus() call.
* @throws LSCMException Thrown indirectly by
* $this->currInstall->addUserFlagFile() call.
* @throws LSCMException Thrown indirectly by Logger::uiSuccess() call.
* @throws LSCMException Thrown indirectly by Logger::notice() call.
* @throws LSCMException Thrown indirectly by
* $this->currInstall->addUserFlagFile() call.
* @throws LSCMException Thrown indirectly by Logger::uiError() call.
* @throws LSCMException Thrown indirectly by Logger::notice() call.
* @throws LSCMException Thrown indirectly by Logger::uiSuccess() call.
* @throws LSCMException Thrown indirectly by Logger::notice() call.
* @throws LSCMException Thrown indirectly by
* $this->currInstall->addUserFlagFile() call.
* @throws LSCMException Thrown indirectly by Logger::uiError() call.
* @throws LSCMException Thrown indirectly by Logger::notice() call.
*/
private function canEnable( $isMassAction, $isNewInstall )
{
$status = $this->getCurrStatus();
if ( $status & WPInstall::ST_PLUGIN_INACTIVE ) {
if ( !$isNewInstall && $isMassAction ) {
$this->currInstall->addUserFlagFile();
Logger::uiSuccess(
'LSCWP Detected As Manually Disabled - Flag Set'
);
Logger::notice(
'Ignore - Previously disabled, flag it from mass operation'
);
return false;
}
$thirdPartyCachePluginSlug =
$this->checkForKnownNonAdvCachePlugins();
if ( $thirdPartyCachePluginSlug != '' ) {
$this->currInstall->addUserFlagFile();
Logger::uiError(
'Cannot Enable LSCWP - Detected another active cache '
. "plugin \"$thirdPartyCachePluginSlug\". Flag set."
);
Logger::notice(
'Ignore - Detected another active cache plugin '
. "\"$thirdPartyCachePluginSlug\". Flagged."
);
return false;
}
}
elseif ( !$isNewInstall ) {
/**
* already active
*/
if ( $status & WPInstall::ST_LSC_ADVCACHE_DEFINED ) {
Logger::uiSuccess('LSCWP Already Enabled - No Action Taken');
Logger::notice('Ignore - Already enabled');
}
else {
$this->currInstall->addUserFlagFile();
Logger::uiError(
'LSCWP Already Enabled But Not Caching - Detected another '
. 'active cache plugin. Please visit the WordPress '
. 'Dashboard for further instructions.'
);
Logger::notice(
'Ignore - Existing install but advanced cache not set'
);
}
return false;
}
return true;
}
/**
*
* @return int
*
* @throws LSCMException Thrown indirectly by $this->canEnable() call.
* @throws LSCMException Thrown indirectly by $this->enable_lscwp() call.
* @throws LSCMException Thrown indirectly by
* $this->directEnableNewInstall() call.
*/
public function directEnable()
{
$isNewInstall = !file_exists($this->pluginEntry);
if ( !$this->canEnable(false, $isNewInstall) ) {
$this->outputResult('STATUS', $this->currInstall->getStatus());
return UserCommand::EXIT_FAIL;
}
if ( !$isNewInstall ) {
$status = $this->enable_lscwp();
}
else {
$status = $this->directEnableNewInstall();
}
$this->outputResult('STATUS', $status);
return UserCommand::EXIT_SUCC;
}
/**
*
* @return int
*
* @throws LSCMException Thrown when unable to extract downloaded LSCWP
* files.
* @throws LSCMException Thrown indirectly by $this->downloadLSCWPZip()
* call.
* @throws LSCMException Thrown indirectly by $this->enable_lscwp() call.
* @throws LSCMException Thrown indirectly by
* $this->removeLscwpPluginFiles() call.
*/
private function directEnableNewInstall()
{
$pluginDir = WpConstants::getWpConstant('WP_PLUGIN_DIR');
$lscwpZip = "$pluginDir/litespeed-cache.latest-stable.zip";
$this->downloadLSCWPZip($lscwpZip);
WpFuncs::WpFilesystem();
$unzipRet = WpFuncs::unzipFile($lscwpZip, $pluginDir);
unlink($lscwpZip);
if ( $unzipRet !== true ) {
throw new LSCMException(
"Unable to extract downloaded LSCWP files.",
LSCMException::E_NON_FATAL
);
}
$this->currInstall->addNewLscwpFlagFile();
$customIni = Context::LOCAL_PLUGIN_DIR . '/'
. PluginVersion::LSCWP_DEFAULTS_INI_FILE_NAME;
if ( file_exists($customIni) ) {
copy(
$customIni,
"$pluginDir/litespeed-cache/data/"
. PluginVersion::LSCWP_DEFAULTS_INI_FILE_NAME
);
}
$this->installedLscwpVer = $this->getPluginVersionFromFile();
$status = $this->enable_lscwp();
if ( $status & WPInstall::ST_PLUGIN_INACTIVE) {
$this->removeLscwpPluginFiles();
}
else {
$this->updateTranslationFiles();
}
return $status;
}
/**
*
* @param string $lscwpZip
*
* @return null
*
* @throws LSCMException Thrown when LSCWP download operation fails.
*/
private function downloadLscwpZip( $lscwpZip )
{
$pluginDir = WpConstants::getWpConstant('WP_PLUGIN_DIR');
$url = 'https://downloads.wordpress.org/plugin/'
. 'litespeed-cache.latest-stable.zip';
exec(
"wget -q --tries=1 --no-check-certificate $url -P $pluginDir",
$output1,
$return_var1
);
if ( $return_var1 === 0 && file_exists($lscwpZip) ) {
return;
}
/**
* Fall back to curl in case wget is disabled for user.
*/
exec(
"cd $pluginDir && curl -O -s --retry 1 --insecure $url",
$output2,
$return_var2
);
if ( $return_var2 === 0 && file_exists($lscwpZip) ) {
return;
}
throw new LSCMException(
"Failed to download LSCWP with wget exit status $return_var1 and "
. "curl exit status $return_var2.",
LSCMException::E_NON_FATAL
);
}
/**
*
* @param array $extraArgs Not used at this time.
* @param bool $massOp True when called from massEnable().
*
* @return int
*
* @throws LSCMException Thrown indirectly by PluginVersion::getInstance()
* call.
* @throws LSCMException Thrown indirectly by
* PluginVersion::getInstance()->prepareUserInstall() call.
* @throws LSCMException Thrown indirectly by $this->canEnable() call.
* @throws LSCMException Thrown indirectly by $this->enable_lscwp() call.
* @throws LSCMException Thrown indirectly by
* $this->removeLscwpPluginFiles() call.
*
* @noinspection PhpUnusedParameterInspection
*/
public function enable( array $extraArgs, $massOp = false )
{
$isNewInstall = PluginVersion::getInstance()->prepareUserInstall(
WpConstants::getWpConstant('WP_PLUGIN_DIR')
);
if ( $isNewInstall ) {
$this->installedLscwpVer = $this->getPluginVersionFromFile();
$this->currInstall->addNewLscwpFlagFile();
}
if ( !$this->canEnable($massOp, $isNewInstall) ) {
$status = $this->currInstall->getStatus();
$ret = UserCommand::EXIT_FAIL;
}
else {
$status = $this->enable_lscwp();
$ret = UserCommand::EXIT_SUCC;
}
if ( $isNewInstall ) {
if ( $status & WPInstall::ST_PLUGIN_INACTIVE ) {
$this->removeLscwpPluginFiles();
}
else {
$this->updateTranslationFiles();
}
}
$this->outputResult('STATUS', $this->currInstall->getStatus());
return $ret;
}
/**
*
* @param string[] $extraArgs
*
* @return int
*
* @throws LSCMException Thrown indirectly by $this->enable() call.
*/
public function massEnable( array $extraArgs )
{
$ret = $this->enable($extraArgs, true);
if ( $this->massIncr != '' ) {
$this->outputResult('MASS_INCR', $this->massIncr);
}
return $ret;
}
/**
*
* @param bool $isMassAction
*
* @return bool
*
* @throws LSCMException Thrown indirectly by $this->getCurrStatus() call.
* @throws LSCMException Thrown indirectly by Logger::notice() call.
* @throws LSCMException Thrown indirectly by Logger::uiSuccess() call.
* @throws LSCMException Thrown indirectly by
* $this->currInstall->addUserFlagFile() call.
* @throws LSCMException Thrown indirectly by Logger::uiSuccess() call.
* @throws LSCMException Thrown indirectly by Logger::notice() call.
* @throws LSCMException Thrown indirectly by
* $this->currInstall->addUserFlagFile() call.
* @throws LSCMException Thrown indirectly by Logger::uiSuccess() call.
*/
private function canDisable( $isMassAction )
{
$status = $this->getCurrStatus();
if ( $status & WPInstall::ST_PLUGIN_INACTIVE ) {
Logger::notice('Ignore - Already disabled');
Logger::uiSuccess(
'LiteSpeed Cache Already Disabled - No Action Taken'
);
return false;
}
if ( $isMassAction ) {
if ( !($status & WPInstall::ST_LSC_ADVCACHE_DEFINED) ) {
$this->currInstall->addUserFlagFile();
Logger::uiSuccess(
'LSCWP Detected As Manually Enabled But Not Caching - Flag '
. 'Set. If desired, this installation can be disabled '
. 'from the Manage Cache Installations screen.'
);
Logger::notice(
'Ignore for mass disable - Installed manually as advanced '
. 'cache not set.'
);
return false;
}
if ( WpFuncs::isPluginActiveForNetwork(self::LSCWP_PLUGIN) ) {
$this->currInstall->addUserFlagFile();
Logger::uiSuccess(
'LiteSpeed Cache Detected As Network Activated - Flag Set '
. '& No Action Taken'
);
return false;
}
}
return true;
}
/**
*
* @param string[] $extraArgs Not used at this time.
* @param bool $massOp True when called from MassDisable().
*
* @return int
*
* @throws LSCMException Thrown indirectly by $this->canDisable() call.
* @throws LSCMException Thrown indirectly by $this->performDisable() call.
* @throws LSCMException Thrown indirectly by Logger::uiSuccess() call.
*
* @noinspection PhpUnusedParameterInspection
*/
public function disable( array $extraArgs, $massOp = false )
{
if ( !$this->canDisable($massOp) ) {
$ret = UserCommand::EXIT_FAIL;
}
else {
if ( $this->performDisable(true) & WPInstall::ST_PLUGIN_ACTIVE ) {
$ret = UserCommand::EXIT_FAIL;
}
else {
Logger::uiSuccess('LiteSpeed Cache Disabled');
$this->massIncr = 'SUCC';
$ret = UserCommand::EXIT_SUCC;
}
}
$this->outputResult('STATUS', $this->currInstall->getStatus());
return $ret;
}
/**
*
* @param string[] $extraArgs
*
* @return int
*
* @throws LSCMException Thrown indirectly by $this->disable() call.
*/
public function massDisable( array $extraArgs )
{
$ret = $this->disable($extraArgs, true);
if ( $this->massIncr != '' ) {
$this->outputResult('MASS_INCR', $this->massIncr);
}
return $ret;
}
/**
* Includes LSCWP files needed to properly disable the LSCWP plugin.
*
* @since 1.13 Removed param $lscwpVer.
*/
private function includeDisableRequiredFiles()
{
$dir = WpConstants::getWpConstant('WP_PLUGIN_DIR') . '/litespeed-cache';
if ( Util::betterVersionCompare($this->installedLscwpVer, '3.0', '>=') ) {
require_once "$dir/src/admin.cls.php";
}
elseif ( Util::betterVersionCompare($this->installedLscwpVer, '1.1.2.2', '>') ) {
require_once "$dir/admin/litespeed-cache-admin.class.php";
}
else {
require_once "$dir/admin/class-litespeed-cache-admin.php";
}
if ( Util::betterVersionCompare($this->installedLscwpVer, '1.1.0', '<')
&& Util::betterVersionCompare($this->installedLscwpVer, '1.0.6', '>') ) {
require_once "$dir/admin/class-litespeed-cache-admin-rules.php";
}
}
/**
*
* @param bool $uninstall
*
* @return int
*
* @throws LSCMException Thrown indirectly by $this->disable_lscwp() call.
*/
private function performDisable( $uninstall )
{
$this->includeDisableRequiredFiles();
return $this->disable_lscwp($uninstall);
}
/**
*
* @param string[] $fromVersions
* @param string $toVersion
* @param bool $massOp Not used at this time.
*
* @return bool
*
* @noinspection PhpUnusedParameterInspection
*/
private function canUpgrade( array $fromVersions, $toVersion, $massOp )
{
if ( !file_exists($this->pluginEntry) ) {
return false;
}
if ( $toVersion == $this->installedLscwpVer ) {
return false;
}
foreach ( $fromVersions as $fromVer ) {
$fromVerParts = explode('.', $fromVer);
$installedVerParts = explode('.', $this->installedLscwpVer);
$i = 0;
while ( isset($fromVerParts[$i]) ) {
$fromVerPart = $fromVerParts[$i];
if ( $fromVerPart == 'x' ) {
return true;
}
if ( !isset($installedVerParts[$i])
|| $installedVerParts[$i] != $fromVerPart ) {
continue 2;
}
$i++;
}
if ( !isset($installedVerParts[$i]) ) {
return true;
}
}
return false;
}
/**
*
* @param string[] $extraArgs
* @param bool $massOp
*
* @return int
*
* @throws LSCMException Thrown indirectly by $this->updateStatus() call.
* @throws LSCMException Thrown indirectly by $this->upgrade_lscwp() call.
*/
public function upgrade( array $extraArgs, $massOp = false )
{
$toVersion = $extraArgs[1];
$canUpgrade = $this->canUpgrade(
explode(',', $extraArgs[0]),
$toVersion,
$massOp
);
if ( !$canUpgrade ) {
$this->updateStatus(true);
return UserCommand::EXIT_FAIL;
}
else {
$this->upgrade_lscwp($toVersion);
return UserCommand::EXIT_SUCC;
}
}
/**
*
* @param string[] $extraArgs
*
* @return int
*
* @throws LSCMException Thrown indirectly by $this->upgrade() call.
*/
public function massUpgrade( array $extraArgs )
{
$ret = $this->upgrade($extraArgs, true);
if ( $this->massIncr != '' ) {
$this->outputResult('MASS_INCR', $this->massIncr);
}
return $ret;
}
/**
*
* @param bool $uninstall
*/
private function deactivate_lscwp( $uninstall )
{
WpFuncs::deactivatePlugins(self::LSCWP_PLUGIN);
if ( $uninstall ) {
//Todo: add some msg about having removed plugin files?
WpFuncs::deletePlugins(array( self::LSCWP_PLUGIN ));
}
}
/**
*
* @param bool $uninstall
*
* @return int
*
* @throws LSCMException Thrown indirectly by $this->getCurrStatus() call.
*/
private function disable_lscwp( $uninstall )
{
if ( WpConstants::getWpConstant('MULTISITE') ) {
$blogs =
Wpdb::getCol("SELECT blog_id FROM " . Wpdb::getBlogs() . ';');
foreach ( $blogs as $id ) {
WpFuncs::switchToBlog($id);
$this->deactivate_lscwp($uninstall);
WpFuncs::restoreCurrentBlog();
}
}
else {
$this->deactivate_lscwp($uninstall);
}
return $this->getCurrStatus();
}
/**
*
* @return int
*
* @throws LSCMException Thrown indirectly by $this->getCurrStatus() call.
* @throws LSCMException Thrown indirectly by $this->performDisable() call.
* @throws LSCMException Thrown indirectly by Logger::uiError() call.
* @throws LSCMException Thrown indirectly by
* $this->currInstall->addUserFlagFile() call.
* @throws LSCMException Thrown indirectly by Logger::uiError() call.
* @throws LSCMException Thrown indirectly by Logger::uiSuccess() call.
*/
private function enable_lscwp()
{
/**
* Should not check directly, can error on success due to object cache.
*/
WpFuncs::activatePlugin(self::LSCWP_PLUGIN);
$status = $this->getCurrStatus();
if ( !($status & WPInstall::ST_LSC_ADVCACHE_DEFINED) ) {
$status = $this->performDisable(true);
Logger::uiError(
'Detected '
. "{$this->currInstall->getPath()}/wp-content/advanced-cache.php "
. 'as belonging to another cache plugin. Please deactivate '
. 'the related cache plugin and try again. You may also '
. 'try manually installing through the WordPress Dashboard '
. 'and following the instructions given.'
);
$this->massIncr = 'FAIL';
}
elseif ( $status & WPInstall::ST_PLUGIN_ACTIVE ) {
if (
!is_writable($this->currInstall->getWpConfigFile())
&&
(
!defined('WP_CACHE')
||
WpConstants::getWpConstant('WP_CACHE') !== true
)
) {
/**
* LSCACHE_ADV_CACHE is incorrectly defined true at this point.
* Detected status must be manually corrected.
*/
$status &= ~WPInstall::ST_LSC_ADVCACHE_DEFINED;
$this->currInstall->setStatus($status);
$this->currInstall->addUserFlagFile();
$status = $this->currInstall->getStatus();
Logger::uiError(
'LSCWP Enabled But Not Caching - Please visit the '
. 'WordPress Dashboard for further instructions.'
);
}
else {
Logger::uiSuccess('LSCWP Enabled');
}
$this->massIncr = 'SUCC';
}
return $status;
}
/**
*
* @param string $ver
* @param bool $runHooks
*
* @throws LSCMException Thrown when LSCWP plugin version upgrade fails.
*/
private function upgrade_lscwp( $ver, $runHooks = true )
{
/**
* Label the following $upgraderWrapper output (Cannot be buffered).
*/
echo "[UPGRADE]\n";
$upgraderWrapper = new PluginUpgrader;
$upgraderWrapper->init();
$upgraderWrapper->upgradeStrings();
$lscwpPackageURL =
"https://downloads.wordpress.org/plugin/litespeed-cache.$ver.zip";
if ( $runHooks ) {
WpFuncs::addFilter(
'upgrader_pre_install',
array(
$upgraderWrapper->getWpPluginUpgraderObject(),
'deactivate_plugin_before_upgrade'
),
10,
2
);
WpFuncs::addFilter(
'upgrader_clear_destination',
array(
$upgraderWrapper->getWpPluginUpgraderObject(),
'delete_old_plugin'
),
10,
4
);
}
$upgraderWrapper->run(
array(
'package' => $lscwpPackageURL,
'destination' =>
WpConstants::getWpConstant('WP_PLUGIN_DIR'),
'clear_destination' => true,
'clear_working' => true,
'hook_extra' => array(
'plugin' => $this->pluginEntry,
'type' => 'plugin',
'action' => 'update',
)
)
);
/**
* Start new messages on a new line
*/
echo "\n";
if ( $runHooks ) {
/**
* Cleanup our hooks, in case something else does an upgrade on
* this connection.
*/
WpFuncs::removeFilter(
'upgrader_pre_install',
array(
$upgraderWrapper->getWpPluginUpgraderObject(),
'deactivate_plugin_before_upgrade'
)
);
WpFuncs::removeFilter(
'upgrader_clear_destination',
array(
$upgraderWrapper->getWpPluginUpgraderObject(),
'delete_old_plugin'
)
);
}
if ( !$upgraderWrapper->getResult()
|| WpFuncs::isWpError($upgraderWrapper->getResult()) ) {
throw new LSCMException(
"Failed to upgrade to v$ver.",
LSCMException::E_NON_FATAL
);
}
$this->updateTranslationFiles();
/**
* Force refresh of plugin update information
*/
WpFuncs::wpCleanPluginsCache();
}
/**
* Gets LSCWP version from the litespeed-cache.php file.
*
* @return string
*/
private function getPluginVersionFromFile()
{
$lscwp_data = WpFuncs::getPluginData($this->pluginEntry, false, false);
return $lscwp_data['Version'];
}
/**
* Checks for local plugin translation files and copies them to the plugin
* languages directory if able. This function will also attempt to inform
* the root user when a locale's translation should be retrieved or removed.
*/
public function updateTranslationFiles()
{
$locale = WpFuncs::getLocale();
if ( $locale == 'en_US' ) {
return;
}
$localTranslationDir = Context::LOCAL_PLUGIN_DIR
. "/$this->installedLscwpVer/translations";
$moFileName = "litespeed-cache-$locale.mo";
$poFileName = "litespeed-cache-$locale.po";
$localMoFile = "$localTranslationDir/$moFileName";
$localPoFile = "$localTranslationDir/$poFileName";
$zipFile = "$localTranslationDir/$locale.zip";
$translationFlag = "$localTranslationDir/"
. PluginVersion::TRANSLATION_CHECK_FLAG_BASE . "_$locale";
$langDir =
$this->currInstall->getPath() . '/wp-content/languages/plugins';
if ( !file_exists($langDir) ) {
mkdir($langDir, 0755);
}
if ( file_exists($localMoFile) && file_exists($localPoFile) ) {
copy($localMoFile, "$langDir/$moFileName");
copy($localPoFile, "$langDir/$poFileName");
}
elseif ( file_exists($zipFile) ) {
WpFuncs::WpFilesystem();
if ( WpFuncs::unzipFile($zipFile, $langDir) !== true ) {
$this->outputResult(
'BAD_TRANSLATION',
"$locale $this->installedLscwpVer"
);
}
}
elseif ( !file_exists($translationFlag)
|| (time() - filemtime($translationFlag)) > 86400 ) {
$this->outputResult(
'GET_TRANSLATION',
"$locale $this->installedLscwpVer"
);
}
}
/**
*
* @since 1.13.4.4
* @since 1.17.0.3 Added required parameter $advCacheFileContents.
*
* @param string $advCacheFileContents
*
* @return bool
*/
private static function advancedCacheFileHasLscacheDefine(
$advCacheFileContents
)
{
$definePosition = strpos($advCacheFileContents, 'LSCACHE_ADV_CACHE');
return ($definePosition !== false);
}
/**
* Check if advanced-cache.php file contains LSCWP placeholder text. This
* file with generic placeholder text was re-added by LSCWP for improved
* compatibility with WordPress versions lower than v5.3.
*
* @since 1.17.0.3
*
* @param string $advCacheFileContent
*
* @return bool
*/
private static function advancedCacheFileHasPlaceholderMsg(
$advCacheFileContent
)
{
$definePosition = strpos(
$advCacheFileContent,
'A compatibility placeholder for WordPress < v5.3'
);
return ($definePosition !== false);
}
private function includeLSCWPAdvancedCacheFile()
{
if ( file_exists($this->advancedCacheFile) ) {
if ( $this->advancedCacheFileHasLscacheDefine() ) {
include_once $this->advancedCacheFile;
}
}
}
/**
*
* @throws LSCMException Thrown indirectly by
* $this->currInstall->removePluginFiles() call.
*/
public function removeLscwpPluginFiles()
{
$this->currInstall->removePluginFiles(dirname($this->pluginEntry));
}
/**
*
* @param string[] $extraArgs
* @param bool $massOp Not used at this time.
*
* @return int
*
* @throws LSCMException Thrown indirectly by
* DashNotifier::prepareUserInstall() call.
* @throws LSCMException Thrown indirectly by DashNotifier::doNotify()
* call.
* @throws LSCMException Thrown indirectly by Logger::uiSuccess() call.
* @throws LSCMException Thrown indirectly by Logger::uiError() call.
*
* @noinspection PhpUnusedParameterInspection
*/
public function dashNotify( array $extraArgs, $massOp = false )
{
if ( DashNotifier::canNotify($this->currInstall->getPath()) ) {
DashNotifier::prepareUserInstall();
if ( DashNotifier::doNotify(base64_decode($extraArgs[0])) ) {
Logger::uiSuccess('Notified Successfully');
$this->massIncr = 'SUCC';
return UserCommand::EXIT_SUCC;
}
Logger::uiError('Failed to Notify');
$this->massIncr = 'FAIL';
}
else {
$this->massIncr = 'BYPASS';
}
return UserCommand::EXIT_FAIL;
}
/**
*
* @param string[] $extraArgs
*
* @return int
*
* @throws LSCMException Thrown indirectly by $this->dashNotify() call.
*/
public function massDashNotify( array $extraArgs )
{
$ret = $this->dashNotify($extraArgs, true);
if ( $this->massIncr != '' ) {
$this->outputResult('MASS_INCR', $this->massIncr);
}
return $ret;
}
/**
*
* @param string[] $extraArgs Unused for now.
* @param bool $massOp Unused for now.
*
* @return int
*
* @throws LSCMException Thrown indirectly by Logger::uiSuccess() call.
*
* @noinspection PhpUnusedParameterInspection
*/
public function dashDisable( array $extraArgs, $massOp = false )
{
if ( WpConstants::getWpConstant('MULTISITE') ) {
$blogs =
Wpdb::getCol('SELECT blog_id FROM ' . Wpdb::getBlogs() . ';');
foreach ( $blogs as $id ) {
WpFuncs::switchToBlog($id);
DashNotifier::deactivate(true);
WpFuncs::restoreCurrentBlog();
}
}
else {
DashNotifier::deactivate(true);
}
if ( WpFuncs::isPluginActive(DashNotifier::DASH_PLUGIN) ) {
$this->massIncr = 'FAIL';
return UserCommand::EXIT_FAIL;
}
Logger::uiSuccess('Dash Notifier Disabled');
$this->massIncr = 'SUCC';
return UserCommand::EXIT_SUCC;
}
/**
*
* @param string[] $extraArgs
*
* @return int
*
* @throws LSCMException Thrown indirectly by $this->dashDisable() call.
*/
public function massDashDisable( array $extraArgs )
{
$ret = $this->dashDisable($extraArgs, true);
if ( $this->massIncr != '' ) {
$this->outputResult('MASS_INCR', $this->massIncr);
}
return $ret;
}
/**
*
* @since 1.12
*
* @param bool $setOutputResult
*
* @return string
*/
public function getQuicCloudAPIKey( $setOutputResult = false )
{
$key = WpFuncs::applyFilters('litespeed_conf', 'api_key', null);
if ( $key == 'api_key' || $key == null ) {
$key = '';
}
if ( $setOutputResult ) {
$this->outputResult('API_KEY', $key);
}
return $key;
}
/**
* Set global server and environment variables.
*
* @since 1.9.8
*
* @param string $key
* @param mixed $val
*/
private function setEnvVar( $key, $val )
{
$_SERVER[$key] = $val;
putenv("$key=$val");
}
/**
* Checks if the current WordPress installation is a multisite install and
* does some preload setup if so.
*
* Patterns and multisite check logic based on WordPress function
* is_multisite().
*
* @since 1.9.8
*
* @return bool
*
* @throws LSCMException Thrown when DOMAIN_CURRENT_SITE is not found with
* MULTISITE defined.
* @throws LSCMException Thrown when PATH_CURRENT_SITE is not found with
* MULTISITE defined.
*/
private function isMultisite()
{
$config_content =
file_get_contents($this->currInstall->getWpConfigFile());
$isMultiSite = (
preg_match(
'/define\(\s*[\'"]MULTISITE[\'"]\s*,[^;]*;/',
$config_content,
$m1
)
&&
preg_match(
'/define\(\s*[\'"]MULTISITE[\'"]\s*,\s*true\s*\)\s*;/',
$m1[0]
)
)
||
(
preg_match(
'/define\(\s*[\'"]SUBDOMAIN_INSTALL[\'"]\s*,[^;]*;/',
$config_content
)
||
preg_match(
'/define\(\s*[\'"]VHOST[\'"]\s*,[^;]*;/',
$config_content
)
||
preg_match(
'/define\(\s*[\'"]SUNRISE[\'"]\s*,[^;]*;/',
$config_content
)
);
if ( !$isMultiSite ) {
return false;
}
$domainCurrentSiteFound = preg_match(
'/define\(\s*[\'"]DOMAIN_CURRENT_SITE[\'"]\s*,'
. '\s*[\'"](.+)[\'"]\s*\)\s*;/',
$config_content,
$m2
);
if ( !$domainCurrentSiteFound ) {
throw new LSCMException(
'Cannot find DOMAIN_CURRENT_SITE with MULTISITE defined.'
);
}
$this->currInstall->setServerName($m2[1]);
$pathCurrentSiteFound = preg_match(
'/define\(\s*[\'"]PATH_CURRENT_SITE[\'"]\s*,'
. '\s*[\'"](.+)[\'"]\s*\)\s*;/',
$config_content,
$m3
);
if ( !$pathCurrentSiteFound ) {
throw new LSCMException(
'Cannot find PATH_CURRENT_SITE with MULTISITE defined.'
);
}
$this->setEnvVar('REQUEST_URI', $m3[1]);
Util::define_wrapper('WP_NETWORK_ADMIN', true);
return true;
}
/**
*
* WP Variables: $wpdb, $shortcode_tags
*
* @global \wpdb $wpdb
* @global array $shortcode_tags
*
* @throws LSCMException Thrown when detected WordPress version is lower
* then 4.0.
* @throws LSCMException Thrown when unable to find a required WordPress
* include file.
* @throws LSCMException Thrown indirectly by $this->isMultisite() call.
*
* @noinspection PhpUndefinedClassInspection
*/
private function initWp()
{
/**
* Declared global variables for use in included files.
*
* @noinspection PhpUnusedLocalVariableInspection
*/
global $wpdb, $shortcode_tags;
error_reporting(E_ALL);
/**
* Attempt to override any WordPress memory limits.
*/
Util::define_wrapper('WP_MEMORY_LIMIT', '512M');
Util::define_wrapper('WP_MAX_MEMORY_LIMIT', '512M');
/**
* Only load core WordPress functionality.
*/
Util::define_wrapper('SHORTINIT', true);
$wpPath = $this->currInstall->getPath();
/**
* Set WP version data global variables, including $wp_version.
*/
include_once "$wpPath/wp-includes/version.php";
/** @noinspection PhpUndefinedVariableInspection $wp_version is defined
* and made available in previous version.php include
*/
if ( Util::betterVersionCompare($wp_version, '4.0', '<') ) {
throw new LSCMException(
"Detected WordPress version as $wp_version. Version 4.0 '
. 'required at minimum."
);
}
/**
* Set needed server variables.
*/
$_SERVER['SCRIPT_FILENAME'] = "$wpPath/wp-admin/plugins.php";
if ( ! $this->isMultisite() ) {
$this->setEnvVar('REQUEST_URI', '');
}
/**
* Set for LSCWP v1.1.5.1+ plugin logic.
*/
if ( $docRoot = $this->currInstall->getDocRoot() ) {
/**
* For enable/disable.
*/
$this->setEnvVar('DOCUMENT_ROOT', $docRoot);
}
$serverName = $this->currInstall->getServerName();
if ( empty($serverName) ) {
$serverName = self::LSCWP_HTTP_HOST_TEST;
}
/**
* For security plugins.
*/
$this->setEnvVar('HTTP_HOST', $serverName);
/**
* Version specific includes may fail on RC releases.
*/
$includeFiles = array(
'/wp-load.php',
'/wp-includes/default-constants.php',
'/wp-includes/formatting.php',
'/wp-includes/meta.php',
'/wp-includes/l10n.php',
'/wp-includes/class-wp-walker.php',
'/wp-includes/capabilities.php'
);
if ( Util::betterVersionCompare($wp_version, '4.4.0', '>=') ) {
$includeFiles[] = '/wp-includes/class-wp-roles.php';
$includeFiles[] = '/wp-includes/class-wp-role.php';
$includeFiles[] = '/wp-includes/class-wp-user.php';
$includeFiles[] = '/wp-includes/rest-api.php';
$includeFiles[] = '/wp-includes/class-wp-http-encoding.php';
$includeFiles[] = '/wp-includes/class-wp-http-proxy.php';
$includeFiles[] = '/wp-includes/class-wp-http-response.php';
$includeFiles[] = '/wp-includes/class-wp-http-curl.php';
$includeFiles[] = '/wp-includes/class-wp-http-cookie.php';
}
$includeFiles[] = '/wp-includes/query.php';
$includeFiles[] = '/wp-includes/theme.php';
$includeFiles[] = '/wp-includes/class-wp-theme.php';
$includeFiles[] = '/wp-includes/user.php';
$includeFiles[] = '/wp-includes/general-template.php';
$includeFiles[] = '/wp-includes/link-template.php';
$includeFiles[] = '/wp-includes/post.php';
$includeFiles[] = '/wp-includes/kses.php';
$includeFiles[] = '/wp-includes/cron.php';
$includeFiles[] = '/wp-includes/update.php';
$includeFiles[] = '/wp-includes/shortcodes.php';
$includeFiles[] = '/wp-includes/http.php';
if ( Util::betterVersionCompare($wp_version, '5.9.0', '>=') ) {
$includeFiles[] = '/wp-includes/class-wp-http.php';
}
else {
$includeFiles[] = '/wp-includes/class-http.php';
}
if ( Util::betterVersionCompare($wp_version, '4.6.0', '>=') ) {
$includeFiles[] = '/wp-includes/class-wp-http-requests-response.php';
}
if ( Util::betterVersionCompare($wp_version, '4.7.0', '>=') ) {
$includeFiles[] = '/wp-includes/class-wp-http-requests-hooks.php';
/**
* Content contained in /wp-includes/query.php for earlier versions.
*/
$includeFiles[] = '/wp-includes/class-wp-query.php';
}
if ( Util::betterVersionCompare($wp_version, '5.0.0', '>=') ) {
$includeFiles[] = '/wp-includes/blocks.php';
$includeFiles[] = '/wp-includes/class-wp-block-parser.php';
}
if ( Util::betterVersionCompare($wp_version, '6.1.0', '>=') ) {
$includeFiles[] = '/wp-includes/class-wp-textdomain-registry.php';
}
$includeFiles[] = '/wp-includes/ms-functions.php';
$includeFiles[] = '/wp-includes/ms-deprecated.php';
$includeFiles[] = '/wp-includes/pluggable.php';
$includeFiles[] = '/wp-admin/includes/plugin.php';
$includeFiles[] = '/wp-admin/includes/file.php';
$includeFiles[] = '/wp-admin/includes/class-wp-upgrader.php';
$includeFiles[] = '/wp-admin/includes/misc.php';
$includeFiles[] = '/wp-admin/includes/template.php';
if ( Util::betterVersionCompare($wp_version, '6.5.0', '>=') ) {
$includeFiles[] = '/wp-includes/class-wp-plugin-dependencies.php';
}
set_error_handler('\Lsc\Wp\WPCaller::warning_handler');
/**
* Force WP to use PHP I/O file handling.
*/
Util::define_wrapper('FS_METHOD', 'direct');
/**
* Trigger an early return from WP Rocket advanced-cache.php to prevent
* WP Rocket from serving a cached copy and killing the process. This
* occurs when calling an action from our control panel plugins and is
* fixed by more closely matching the environment of a direct cli call.
*/
unset($_SERVER['REQUEST_METHOD']);
self::redefineDisabledFunctions();
foreach ( $includeFiles as $file ) {
$file = $wpPath . $file;
if ( !file_exists($file) ) {
throw new LSCMException(
"Could not include missing file $file."
);
}
include_once $file;
}
restore_error_handler();
/**
* Needs to be defined after including files.
*/
Util::define_wrapper('WP_ADMIN', true);
/**
* Define common WP constants and set 'wp_plugin_paths' array.
*/
WpFuncs::wpPluginDirectoryConstants();
/**
* Do not load other plugins.
*/
$GLOBALS['wp_plugin_paths'] = array();
WpFuncs::wpCookieConstants();
/**
* Create global wp_query (WordPress) object entry. Needed during
* LSCWP uninstall.
*/
$wpQueryWrapper = new WpQuery();
$GLOBALS['wp_the_query'] = $wpQueryWrapper->getWpWpQueryObject();
$GLOBALS['wp_query'] = $GLOBALS['wp_the_query'];
if ( Util::betterVersionCompare($wp_version, '6.1.0', '>=') ) {
$wpTextdomainRegistryWrapper = new WpTextdomainRegistry();
$GLOBALS['wp_textdomain_registry'] =
$wpTextdomainRegistryWrapper->getWpWpTextdomainRegistryObject();
}
$this->pluginEntry = WpConstants::getWpConstant('WP_PLUGIN_DIR')
. '/' . self::LSCWP_PLUGIN;
if ( $this->loadLscwp && file_exists($this->pluginEntry) ) {
include $this->pluginEntry;
$this->installedLscwpVer = $this->getPluginVersionFromFile();
if ( Util::betterVersionCompare($this->installedLscwpVer, '3.0.4', '<') ) {
$this->includeLSCWPAdvancedCacheFile();
}
}
}
}