File: //proc/self/cwd/wp-content/plugins/duplicator-pro/src/Utils/UsageStatistics/PluginData.php
<?php
namespace Duplicator\Utils\UsageStatistics;
use DUP_PRO_DB;
use DUP_PRO_Package;
use DUP_PRO_Package_Template_Entity;
use DUP_PRO_PackageStatus;
use DUP_PRO_PackageType;
use DUP_PRO_Schedule_Entity;
use Duplicator\Addons\ProBase\License\License;
use Duplicator\Core\CapMng;
use Duplicator\Core\MigrationMng;
use Duplicator\Core\Upgrade\UpgradePlugin;
use Duplicator\Installer\Core\InstState;
use Duplicator\Installer\Models\MigrateData;
use Duplicator\Libs\Snap\SnapDB;
use Duplicator\Libs\Snap\SnapUtil;
use Duplicator\Libs\Snap\SnapWP;
use Duplicator\Models\BrandEntity;
use Duplicator\Models\Storages\AbstractStorageEntity;
use Duplicator\Models\Storages\Local\LocalStorage;
use Duplicator\Package\Create\BuildComponents;
use Duplicator\Package\Recovery\RecoveryPackage;
use VendorDuplicator\Amk\JsonSerialize\JsonSerialize;
use wpdb;
class PluginData
{
const PLUGIN_DATA_OPTION_KEY = 'duplicator_pro_plugin_data_stats';
const IDENTIFIER_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-.,;=+&';
const PLUGIN_STATUS_ACTIVE = 'active';
const PLUGIN_STATUS_INACTIVE = 'inactive';
/**
* @var ?self
*/
private static $instance;
/**
* @var int
*/
private $lastSendTime = 0;
/**
* @var string
*/
private $identifier = '';
/**
* @var string
*/
private $plugin = 'dup-pro';
/**
* @var string
*/
private $pluginStatus = self::PLUGIN_STATUS_ACTIVE;
/**
* @var bool
*/
private $anonymous = false;
/**
* @var int
*/
private $buildCount = 0;
/**
* @var int
*/
private $buildLastDate = 0;
/**
* @var int
*/
private $buildFailedCount = 0;
/**
* @var int
*/
private $buildFailedLastDate = 0;
/**
* @var int
*/
private $packagesBuildCompFullCount = 0;
/**
* @var int
*/
private $packagesBuildCompDbOnlyCount = 0;
/**
* @var int
*/
private $packagesBuildCompMdOnlyCount = 0;
/**
* @var int
*/
private $packagesBuildCompCustomCount = 0;
/**
* @var int
*/
private $packagesBuildCompCustomOnlyActiveCount = 0;
/**
* @var int
*/
private $schedulesBuildCount = 0;
/**
* @var int
*/
private $schedulesBuildLastDate = 0;
/**
* @var int
*/
private $schedulesBuildFailedCount = 0;
/**
* @var int
*/
private $schedulesBuildFailedLastDate = 0;
/**
* @var int
*/
private $schedulesBuildCompFullCount = 0;
/**
* @var int
*/
private $schedulesBuildCompDbOnlyCount = 0;
/**
* @var int
*/
private $schedulesBuildCompMdOnlyCount = 0;
/**
* @var int
*/
private $schedulesBuildCompCustomCount = 0;
/**
* @var int
*/
private $schedulesBuildCompCustomOnlyActiveCount = 0;
/**
* @var int
*/
private $usedRecoveryCount = 0;
/**
* @var float
*/
private $siteSizeMB = 0;
/**
* @var int
*/
private $siteNumFiles = 0;
/**
* @var float
*/
private $siteDbSizeMB = 0;
/**
* @var int
*/
private $siteDbNumTables = 0;
/**
* Class constructor
*/
private function __construct()
{
if (($data = get_option(self::PLUGIN_DATA_OPTION_KEY)) !== false) {
JsonSerialize::unserializeToObj($data, $this);
} else {
$this->identifier = self::generateIdentifier();
$this->save();
}
}
/**
* Get instance
*
* @return self
*/
public static function getInstance()
{
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Save plugin data
*
* @return bool True if data has been saved, false otherwise
*/
public function save()
{
return update_option(self::PLUGIN_DATA_OPTION_KEY, JsonSerialize::serialize($this, JSON_PRETTY_PRINT));
}
/**
* Get identifier
*
* @return string
*/
public function getIdentifier()
{
return $this->identifier;
}
/**
* Update from migrate data
*
* @param MigrateData $data Migration data
*
* @return bool
*/
public function updateFromMigrateData(MigrateData $data)
{
$save = false;
if (
strlen($data->ustatIdentifier) > 0 &&
$data->ustatIdentifier !== $this->identifier
) {
$this->identifier = $data->ustatIdentifier;
$save = true;
}
if ($data->recoveryMode) {
$this->usedRecoveryCount++;
$save = true;
}
return ($save ? $this->save() : true);
}
/**
* Return usage tracking data
*
* @return array<string, mixed>
*/
public function getDataToSend()
{
$result = $this->getBasicInfos();
$result = array_merge($result, $this->getPluginInfos());
$result = array_merge($result, $this->getSiteInfos());
$result = array_merge($result, $this->getManualPackageInfos());
$result = array_merge($result, $this->getStoragesInfos());
$result = array_merge($result, $this->getTemplatesInfos());
$result = array_merge($result, $this->getSchedulesInfos());
$result = array_merge($result, $this->getGranularPermissionsInfos());
$result = array_merge($result, $this->getSettingsInfos());
$result = array_merge($result, $this->getOtherInfos());
if (!$this->anonymous) {
$result = array_merge($result, $this->getNonAnonymousInfos());
}
$rules = [
'api_version' => 'string|max:7', // 1.0
'identifier' => 'string|max:44',
// BASIC INFO
'plugin_version' => 'string|max:25',
'php_version' => 'string|max:25',
'wp_version' => 'string|max:25',
// PLUGIN INFO
'pinstall_version' => '?string|max:25',
// SITE INFO
'servertype' => 'string|max:25',
'db_engine' => 'string|max:25',
'db_version' => 'string|max:25',
'timezoneoffset' => 'string|max:10',
'locale' => 'string|max:10',
'themename' => 'string|max:255',
'themeversion' => 'string|max:25',
// NON-ANONYMOUS INFO
'email' => '?string|max:255',
'api_key' => '?string|max:32',
];
return StatsUtil::sanitizeFields($result, $rules);
}
/**
* Get disable tracking data
*
* @return array<string, mixed>
*/
public function getDisableDataToSend()
{
$result = $this->getBasicInfos();
$rules = [
'api_version' => 'string|max:7', // 1.0
'identifier' => 'string|max:44',
// BASIC INFO
'plugin_version' => 'string|max:25',
'php_version' => 'string|max:25',
'wp_version' => 'string|max:25',
];
return StatsUtil::sanitizeFields($result, $rules);
}
/**
* Set status
*
* @param string $status Status: active, inactive or uninstalled
*
* @return void
*/
public function setStatus($status)
{
if ($this->pluginStatus === $status) {
return;
}
switch ($status) {
case self::PLUGIN_STATUS_ACTIVE:
case self::PLUGIN_STATUS_INACTIVE:
$this->pluginStatus = $status;
$this->save();
break;
}
}
/**
* Get status
*
* @return string Enum: self::PLUGIN_STATUS_ACTIVE, self::PLUGIN_STATUS_INACTIVE or self::PLUGIN_STATUS_UNINSTALLED
*/
public function getStatus()
{
return $this->pluginStatus;
}
/**
* Add paackage build count and date for manual and schedule build
*
* @param DUP_PRO_Package $package Backup
*
* @return void
*/
public function addPackageBuild(DUP_PRO_Package $package)
{
if ($package->Type == DUP_PRO_PackageType::MANUAL) {
if ($package->Status == DUP_PRO_PackageStatus::COMPLETE) {
$this->buildCount++;
$this->buildLastDate = time();
switch (BuildComponents::getActionFromComponents($package->components)) {
case BuildComponents::COMP_ACTION_ALL:
$this->packagesBuildCompFullCount++;
break;
case BuildComponents::COMP_ACTION_DB:
$this->packagesBuildCompDbOnlyCount++;
break;
case BuildComponents::COMP_ACTION_MEDIA:
$this->packagesBuildCompMdOnlyCount++;
break;
case BuildComponents::COMP_ACTION_CUSTOM:
if (
array_intersect(
$package->components,
[
BuildComponents::COMP_PLUGINS_ACTIVE,
BuildComponents::COMP_THEMES_ACTIVE,
]
)
) {
$this->packagesBuildCompCustomOnlyActiveCount++;
} else {
$this->packagesBuildCompCustomCount++;
}
break;
}
} else {
$this->buildFailedCount++;
$this->buildFailedLastDate = time();
}
} else {
if ($package->Status == DUP_PRO_PackageStatus::COMPLETE) {
$this->schedulesBuildCount++;
$this->schedulesBuildLastDate = time();
switch (BuildComponents::getActionFromComponents($package->components)) {
case BuildComponents::COMP_ACTION_ALL:
$this->schedulesBuildCompFullCount++;
break;
case BuildComponents::COMP_ACTION_DB:
$this->schedulesBuildCompDbOnlyCount++;
break;
case BuildComponents::COMP_ACTION_MEDIA:
$this->schedulesBuildCompMdOnlyCount++;
break;
case BuildComponents::COMP_ACTION_CUSTOM:
if (
array_intersect(
$package->components,
[
BuildComponents::COMP_PLUGINS_ACTIVE,
BuildComponents::COMP_THEMES_ACTIVE,
]
)
) {
$this->schedulesBuildCompCustomOnlyActiveCount++;
} else {
$this->schedulesBuildCompCustomCount++;
}
break;
}
} else {
$this->schedulesBuildFailedCount++;
$this->schedulesBuildFailedLastDate = time();
}
}
$this->save();
}
/**
* Set site size
*
* @param int $size Site size in bytes
* @param int $numFiles Number of files
* @param int $dbSize Database size in bytes
* @param int $numTables Number of tables
*
* @return void
*/
public function setSiteSize($size, $numFiles, $dbSize, $numTables)
{
$this->siteSizeMB = round(((int) $size) / 1024 / 1024, 2);
$this->siteNumFiles = (int) $numFiles;
$this->siteDbSizeMB = round(((int) $dbSize) / 1024 / 1024, 2);
$this->siteDbNumTables = (int) $numTables;
$this->save();
}
/**
* Update last send time
*
* @return void
*/
public function updateLastSendTime()
{
$this->lastSendTime = time();
$this->save();
}
/**
* Get last send time
*
* @return int
*/
public function getLastSendTime()
{
return $this->lastSendTime;
}
/**
* Get basic infos
*
* @return array<string, mixed>
*/
protected function getBasicInfos()
{
return [
'api_version' => CommStats::API_VERSION,
'identifier' => $this->identifier,
'plugin' => $this->plugin,
'plugin_status' => $this->pluginStatus,
'plugin_version' => DUPLICATOR_PRO_VERSION,
'php_version' => SnapUtil::getVersion(phpversion(), 3),
'wp_version' => get_bloginfo('version'),
];
}
/**
* Return plugin infos
*
* @return array<string, mixed>
*/
protected function getPluginInfos()
{
$installInfo = UpgradePlugin::getInstallInfo();
return [
'pinstall_date' => (isset($installInfo['time']) ? date('Y-m-d H:i:s', $installInfo['time']) : null),
'pinstall_version' => ($installInfo['version'] ?? null),
'license_type' => StatsUtil::getLicenseType(),
'license_status' => StatsUtil::getLicenseStatus(),
];
}
/**
* Return non-anonymous infos
*
* @return array<string, mixed>
*/
protected function getNonAnonymousInfos()
{
return [
'email' => get_bloginfo('admin_email'),
'api_key' => (strlen(License::getLicenseKey()) ? License::getLicenseKey() : null),
];
}
/**
* Return site infos
*
* @return array<string, mixed>
*/
protected function getSiteInfos()
{
/** @var wpdb $wpdb */
global $wpdb;
$theme_data = wp_get_theme();
return [
'servertype' => StatsUtil::getServerType(),
'db_engine' => SnapDB::getDBEngine($wpdb->dbh), // @phpstan-ignore-line
'db_version' => DUP_PRO_DB::getVersion(),
'is_multisite' => is_multisite(),
'sites_count' => count(SnapWP::getSitesIds()),
'user_count' => SnapWp::getUsersCount(),
'timezoneoffset' => get_option('gmt_offset'), /** @todo evaluate use wp or server timezone offset */
'locale' => get_locale(),
'am_family' => StatsUtil::getAmFamily(),
'themename' => $theme_data->get('Name'),
'themeversion' => $theme_data->get('Version'),
'site_size_mb' => ($this->siteSizeMB == 0 ? null : $this->siteSizeMB),
'site_num_files' => ($this->siteNumFiles == 0 ? null : $this->siteNumFiles),
'site_db_size_mb' => ($this->siteDbSizeMB == 0 ? null : $this->siteDbSizeMB),
'site_db_num_tbl' => ($this->siteDbNumTables == 0 ? null : $this->siteDbNumTables),
];
}
/**
* Return manal Backup infos
*
* @return array<string, mixed>
*/
protected function getManualPackageInfos()
{
return [
'packages_build_count' => $this->buildCount,
'packages_build_last_date' => ($this->buildLastDate == 0 ? null : date('Y-m-d H:i:s', $this->buildLastDate)),
'packages_build_failed_count' => $this->buildFailedCount,
'packages_build_failed_last_date' => ($this->buildFailedLastDate == 0 ? null : date('Y-m-d H:i:s', $this->buildFailedLastDate)),
'packages_build_comp_full_count' => $this->packagesBuildCompFullCount,
'packages_build_comp_dbonly_count' => $this->packagesBuildCompDbOnlyCount,
'packages_build_comp_mdonly_count' => $this->packagesBuildCompMdOnlyCount,
'packages_build_comp_custom_count' => $this->packagesBuildCompCustomCount,
'packages_build_comp_custom_only_active_count' => $this->packagesBuildCompCustomOnlyActiveCount,
'packages_count' => DUP_PRO_Package::getNumCompletePackages(),
];
}
/**
* Return storages infos
*
* @return array<string, mixed>
*/
protected function getStoragesInfos()
{
$result = [
'storages_local_count' => 0,
'storages_onedrive_count' => 0,
'storages_s3_count' => 0,
'storages_s3_compatible_count' => 0,
'storages_backblaze_count' => 0,
'storages_tot_count' => 0,
];
if (($storages = AbstractStorageEntity::getAll()) === false) {
$storages = [];
}
foreach ($storages as $index => $storage) {
if ($index === 0) {
// Skip default local storage
continue;
}
if ($storage->getSType() == LocalStorage::getSType()) {
$result['storages_local_count']++;
}
}
$result = apply_filters('duplicator_usage_stats_storages_infos', $result);
$total = 0;
foreach ($result as $value) {
$total += $value;
}
$result['storages_tot_count'] = $total;
return $result;
}
/**
* return template infos
*
* @return array<string, mixed>
*/
protected function getTemplatesInfos(): array
{
$result = [
'can_use_adv_components' => License::can(License::CAPABILITY_PACKAGE_COMPONENTS_PLUS),
'package_manual_create_component' => 'full',
'templates_full_count' => 0,
'templates_dbonly_count' => 0,
'templates_monly_count' => 0,
'templates_custom_count' => 0,
'templates_custom_only_active_count' => 0,
'templates_tot_count' => 0,
];
$manualTpl = DUP_PRO_Package_Template_Entity::get_manual_template();
switch (BuildComponents::getActionFromComponents($manualTpl->components)) {
case BuildComponents::COMP_ACTION_ALL:
$result['package_manual_create_component'] = 'full';
break;
case BuildComponents::COMP_ACTION_DB:
$result['package_manual_create_component'] = 'dbonly';
break;
case BuildComponents::COMP_ACTION_MEDIA:
$result['package_manual_create_component'] = 'mediaonly';
break;
case BuildComponents::COMP_ACTION_CUSTOM:
if (
array_intersect(
$manualTpl->components,
[
BuildComponents::COMP_PLUGINS_ACTIVE,
BuildComponents::COMP_THEMES_ACTIVE,
]
)
) {
$result['package_manual_create_component'] = 'custom_only_active';
} else {
$result['package_manual_create_component'] = 'custom';
}
break;
}
if (($templates = DUP_PRO_Package_Template_Entity::getAllWithoutManualMode()) === false) {
$templates = [];
}
foreach ($templates as $tpl) {
switch (BuildComponents::getActionFromComponents($tpl->components)) {
case BuildComponents::COMP_ACTION_ALL:
$result['templates_full_count']++;
break;
case BuildComponents::COMP_ACTION_DB:
$result['templates_dbonly_count']++;
break;
case BuildComponents::COMP_ACTION_MEDIA:
$result['templates_monly_count']++;
break;
case BuildComponents::COMP_ACTION_CUSTOM:
if (
array_intersect(
$tpl->components,
[
BuildComponents::COMP_PLUGINS_ACTIVE,
BuildComponents::COMP_THEMES_ACTIVE,
]
)
) {
$result['templates_custom_only_active_count']++;
} else {
$result['templates_custom_count']++;
}
break;
}
$result['templates_tot_count']++;
}
return $result;
}
/**
* Return schedules infos
*
* @return array<string, mixed>
*/
protected function getSchedulesInfos(): array
{
$result = [
'can_use_adv_schedules' => License::can(License::CAPABILITY_SHEDULE_HOURLY),
'schedules_hourly_count' => 0,
'schedules_daily_count' => 0,
'schedules_weekly_count' => 0,
'schedules_monthly_count' => 0,
'schedules_disabled_count' => 0,
'schedules_enabled_count' => 0,
'schedules_build_count' => $this->schedulesBuildCount,
'schedules_build_last_date' => ($this->schedulesBuildLastDate == 0 ? null : date('Y-m-d H:i:s', $this->schedulesBuildLastDate)),
'schedules_build_failed_count' => $this->schedulesBuildFailedCount,
'schedules_build_failed_last_date' => (
$this->schedulesBuildFailedLastDate == 0 ? null : date('Y-m-d H:i:s', $this->schedulesBuildFailedLastDate)
),
'schedules_build_comp_full_count' => $this->schedulesBuildCompFullCount,
'schedules_build_comp_dbonly_count' => $this->schedulesBuildCompDbOnlyCount,
'schedules_build_comp_mdonly_count' => $this->schedulesBuildCompMdOnlyCount,
'schedules_build_comp_custom_count' => $this->schedulesBuildCompCustomCount,
'schedules_build_comp_custom_only_active_count' => $this->schedulesBuildCompCustomOnlyActiveCount,
];
if (($schedules = DUP_PRO_Schedule_Entity::getAll()) === false) {
$schedules = [];
}
foreach ($schedules as $schedule) {
if (!$schedule->active) {
$result['schedules_disabled_count']++;
continue;
}
$result['schedules_enabled_count']++;
switch ($schedule->repeat_type) {
case DUP_PRO_Schedule_Entity::REPEAT_HOURLY:
$result['schedules_hourly_count']++;
break;
case DUP_PRO_Schedule_Entity::REPEAT_DAILY:
$result['schedules_daily_count']++;
break;
case DUP_PRO_Schedule_Entity::REPEAT_MONTHLY:
$result['schedules_monthly_count']++;
break;
case DUP_PRO_Schedule_Entity::REPEAT_WEEKLY:
$result['schedules_weekly_count']++;
break;
}
}
return $result;
}
/**
* Return granular permissions infos
*
* @return array<string, mixed>
*/
protected function getGranularPermissionsInfos()
{
return [
'can_use_adv_permissions' => License::can(License::CAPABILITY_CAPABILITIES_MNG_PLUS),
'have_custom_adv_permissions' => !CapMng::getInstance()->isDefault(),
'have_custom_users_adv_permissions' => CapMng::getInstance()->hasUsersCapabilities(),
];
}
/**
* Return granular permissions infos
*
* @return array<string, mixed>
*/
protected function getSettingsInfos()
{
return [
'settings_archive_build_mode' => StatsUtil::getArchiveBuildMode(),
'settings_db_build_mode' => StatsUtil::getDbBuildMode(),
'settings_usage_enabled' => StatsBootstrap::isTrackingAllowed(),
];
}
/**
* Return other infos
*
* @return array<string, mixed>
*/
protected function getOtherInfos()
{
$migrateData = MigrationMng::getMigrationData();
if (($brands = BrandEntity::getAll()) == false) {
$brands = [];
}
return [
'is_recovery_point_set' => (RecoveryPackage::getRecoverPackageId() !== false),
'used_recovery_point_count' => $this->usedRecoveryCount,
'branding_count' => count($brands),
'is_recovered_site' => $migrateData->recoveryMode,
'is_migrated_site' => ($migrateData->installType !== InstState::TYPE_NOT_SET),
];
}
/**
* Return unique identifier
*
* @return string
*/
protected static function generateIdentifier()
{
$maxRand = strlen(self::IDENTIFIER_CHARS) - 1;
$result = '';
for ($i = 0; $i < 44; $i++) {
$result .= substr(self::IDENTIFIER_CHARS, wp_rand(0, $maxRand), 1);
}
return $result;
}
}