Файловый менеджер - Редактировать - /home/freeclou/app.optimyar.com/front-web/build/assets/fonts/iran-yekan/beflpn/Components.tar
Назад
Ajax.php 0000755 00000000501 15105501144 0006133 0 ustar 00 <?php namespace WP_Statistics\Components; class Ajax { public static function register($action, $callback, $public = true) { add_action('wp_ajax_wp_statistics_' . $action, $callback); if ($public) { add_action('wp_ajax_nopriv_wp_statistics_' . $action, $callback); } } } AssetNameObfuscator.php 0000755 00000024235 15105501144 0011172 0 ustar 00 <?php namespace WP_Statistics\Components; use WP_Statistics; use WP_STATISTICS\Helper; use WP_STATISTICS\Option; /** * Ofuscates/Randomizes assets file names. */ class AssetNameObfuscator { /** * Option that contains information about all hashed files. * * @var string */ private $optionName = 'hashed_assets'; /** * All hashed files. * * @var array */ private $hashedAssetsArray = []; /** * Hashed file's key in options (which is its path relative to `WP_STATISTICS_DIR`). * * @var string */ private $hashedFileOptionKey; /** * @var string */ private $inputFileDir; /** * WordPress /plugins/ directory. * * @var string */ private $pluginsRoot; /** * MD5 hashed string of plugin's version + actual file name. * * @var string */ private $hashedFileName; /** * Root of the hash files dir. * * @var string */ private $hashedFilesRootDir; /** * Full dir of the hashed file. * * @var string */ private $hashedFileDir; /** * @param string $file Full path of the input file. * Pass `null` if you only want to use `deleteAllHashedFiles` and `deleteDatabaseOption` methods. (e.g. When uninstalling the plugin) * * @return void */ public function __construct($file = null) { // Handle slashes $this->inputFileDir = !empty($file) ? wp_normalize_path($file) : ''; $this->pluginsRoot = wp_normalize_path(WP_PLUGIN_DIR . DIRECTORY_SEPARATOR); if (stripos($this->inputFileDir, $this->pluginsRoot) === false) { $this->inputFileDir = path_join($this->pluginsRoot, $this->inputFileDir); } if (!is_file($this->inputFileDir)) return; $this->initializeVariables(); $this->obfuscateFileName(); } /** * Initializes class variables. * * @return void */ private function initializeVariables() { $this->hashedAssetsArray = Option::getOptionGroup($this->optionName, null, []); $this->hashedFileOptionKey = str_replace($this->pluginsRoot, '', $this->inputFileDir); if (empty($this->hashedAssetsArray[$this->hashedFileOptionKey])) { $this->hashedAssetsArray[$this->hashedFileOptionKey] = []; $this->hashedAssetsArray[$this->hashedFileOptionKey]['version'] = WP_STATISTICS_VERSION; } $this->hashedFileName = $this->generateShortHash(WP_STATISTICS_VERSION . $this->hashedFileOptionKey); $this->hashedFileName .= '.' . pathinfo($this->inputFileDir, PATHINFO_EXTENSION); $this->hashedFileName = $this->cleanHashedFileName($this->hashedFileName); $this->hashedFileName = apply_filters('wp_statistics_hashed_asset_name', $this->hashedFileName, $this->inputFileDir); $this->hashedFilesRootDir = apply_filters('wp_statistics_hashed_asset_root', Helper::get_uploads_dir()); if (!is_dir($this->hashedFilesRootDir)) { // Try to make the filtered dir if it not exists if (!mkdir($this->hashedFilesRootDir, 0700)) { // Revert back to default uploads folder if the filtered dir is invalid $this->hashedFilesRootDir = Helper::get_uploads_dir(); } } $this->hashedFileDir = $this->isHashedFileExists() ? $this->hashedAssetsArray[$this->hashedFileOptionKey]['dir'] : path_join($this->hashedFilesRootDir, $this->hashedFileName); $this->hashedFileDir = apply_filters('wp_statistics_hashed_asset_dir', $this->hashedFileDir, $this->hashedFilesRootDir, $this->hashedFileName); } /** * Generates a truncated MD5 hash of the input string. * * @param string $input The input string to be hashed. * @param int $length The length of the truncated hash. * @return string The truncated MD5 hash. */ private function generateShortHash($input, $length = 10) { $hash = wp_hash($input); return substr($hash, 0, $length); } /** * Obfuscate/Randomize file name. * * @return void */ private function obfuscateFileName() { // Return if the hashed file for this version exists if ($this->isHashedFileExists()) return; // Delete old file $this->deleteHashedFile($this->hashedAssetsArray, $this->hashedFileOptionKey); // Copy and randomize the name of the input file if (!copy($this->inputFileDir, $this->getHashedFileDir())) { WP_Statistics::log("Unable to copy hashed file to {$this->getHashedFileDir()}!", 'warning'); return; } $this->hashedAssetsArray[$this->hashedFileOptionKey]['version'] = WP_STATISTICS_VERSION; $this->hashedAssetsArray[$this->hashedFileOptionKey]['dir'] = $this->getHashedFileDir(); Option::saveOptionGroup($this->hashedFileOptionKey, $this->hashedAssetsArray[$this->hashedFileOptionKey], $this->optionName); } /** * Checks to see if a hashed/randomized file for this version already exists or not. * * @return bool */ private function isHashedFileExists() { return $this->hashedAssetsArray[$this->hashedFileOptionKey]['version'] === WP_STATISTICS_VERSION && !empty($this->hashedAssetsArray[$this->hashedFileOptionKey]['dir']) && file_exists($this->hashedAssetsArray[$this->hashedFileOptionKey]['dir']); } /** * Returns hashed file name. * * @return string */ public function getHashedFileName() { return $this->hashedFileName; } /** * Returns hashed files root dir. * * @return string */ public function getHashedFilesRootDir() { return $this->hashedFilesRootDir; } /** * Returns full path (DIR) of the hashed file. * * @return string */ public function getHashedFileDir() { return $this->hashedFileDir; } /** * Returns full URL of the hashed file. * * @return string */ public function getHashedFileUrl() { return Helper::get_upload_url() . '/' . $this->hashedFileName; } /** * Generates a dynamic query parameter based on the hashed domain URL. * This helps to avoid conflicts with other plugins and prevents ad-blocking issues. * * @return string The dynamic query parameter. */ public function getDynamicAssetKey() { return $this->generateShortHash(home_url(), 6); } /** * Generates a URL to serve the asset through a proxy. * * @return string */ public function getUrlThroughProxy() { return esc_url(home_url('?' . $this->getDynamicAssetKey() . '=' . $this->hashedFileName)); } /** * Deletes a hashed file. * * @param array $assetsArray All hashed files. * @param string $key Hashed file's key (which is its path relative to `WP_STATISTICS_DIR`). * * @return void */ private function deleteHashedFile($assetsArray, $key) { if (!empty($assetsArray[$key]) && !empty($assetsArray[$key]['dir']) && file_exists($assetsArray[$key]['dir'])) { unlink($assetsArray[$key]['dir']); } } /** * Deletes all hashed files. * * @return void */ public function deleteAllHashedFiles() { // Method was called from uninstall probably, initialize the array again $hashedAssetsArray = Option::getOptionGroup($this->optionName, null, []); foreach ($hashedAssetsArray as $key => $asset) { $this->deleteHashedFile($hashedAssetsArray, $key); } } /** * Deletes `wp_statistics_hashed_assets` option from the database. * * @return void */ public function deleteDatabaseOption() { delete_option('wp_statistics_hashed_assets'); } /** * Proxies requested asset files through PHP to serve them securely. * * @param string $asset * * @return void */ public function serveAssetByHash($asset) { $asset = $this->cleanHashedFileName($asset); $hashedAssetsArray = Option::getOptionGroup($this->optionName, null, []); $originalFilePath = $this->getHashedAssetPath($asset, $hashedAssetsArray); if ($originalFilePath && file_exists($originalFilePath)) { $extension = pathinfo($originalFilePath, PATHINFO_EXTENSION); $mimeTypes = [ 'js' => 'application/javascript', 'css' => 'text/css', ]; $contentType = isset($mimeTypes[$extension]) ? $mimeTypes[$extension] : 'application/octet-stream'; header("Content-Type: $contentType"); header('Cache-Control: public, max-age=86400'); readfile($originalFilePath); exit(); } else { wp_die(__('File not found.', 'wp-statistics'), __('404 Not Found', 'wp-statistics'), array('response' => 404)); } } /** * Retrieves the original file path based on a hashed file name. * * @param string $hashedFileName * * @param array $hashedAssetsArray * * @return string|null */ private function getHashedAssetPath($hashedFileName, $hashedAssetsArray) { if (!empty($hashedAssetsArray)) { foreach ($hashedAssetsArray as $originalPath => $info) { if (isset($info['dir']) && basename($info['dir']) === $hashedFileName) { return WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . $originalPath; } } } return null; } /** * Clean the file name by removing any extra data * * @param string $hashedFileName * * @return string */ private function cleanHashedFileName($hashedFileName) { $posJs = strpos($hashedFileName, '.js'); if ($posJs !== false) { return substr($hashedFileName, 0, $posJs + 3); } $posCss = strpos($hashedFileName, '.css'); if ($posCss !== false) { return substr($hashedFileName, 0, $posCss + 4); } return $hashedFileName; } } Assets.php 0000755 00000015427 15105501144 0006527 0 ustar 00 <?php namespace WP_Statistics\Components; use WP_STATISTICS\Helper; class Assets { /** * Assets folder name * * @var string */ public static $asset_dir = 'assets'; /** * Plugin Url in WordPress * * @var string * @example http://site.com/wp-content/plugins/my-plugin/ */ public static $plugin_url = WP_STATISTICS_URL; /** * Plugin DIR in WordPress * * @var string * @example http://srv/www/wp.site/wp-content/plugins/my-plugin/ */ public static $plugin_dir = WP_STATISTICS_DIR; /** * Check if a script has been enqueued or not * * @param string $handle The script handle. * * @return bool */ public static function isScriptEnqueued($handle) { $handle = self::getHandle($handle); return wp_script_is($handle, 'enqueued'); } /** * Enqueue a script. * * @param string $handle The script handle. * @param string $src The source URL of the script. * @param array $deps An array of script dependencies. * @param array $localize An array of data to be localized. * @param bool $inFooter Whether to enqueue the script in the footer. * @param bool $obfuscate Ofuscate/Randomize asset's file name. * @param string $pluginUrl The plugin URL. * @param string $version Script version number. * @param string $strategy Loading strategy. * * @return void * @example Assets::script('admin', 'dist/admin.js', ['jquery'], ['foo' => 'bar'], true, false, WP_STATISTICS_URL, '1.0.0'); */ public static function script($handle, $src, $deps = [], $localize = [], $inFooter = false, $obfuscate = false, $pluginUrl = null, $version = '', $strategy = '') { $strategy = apply_filters("wp_statistics_{$handle}_loading_strategy", $strategy); $object = self::getObject($handle); $handle = self::getHandle($handle); $version = empty($version) ? WP_STATISTICS_VERSION : trim($version); $args = $inFooter; global $wp_version; $supportStrategy = version_compare($wp_version, '6.3', '>='); if ($supportStrategy && !empty($strategy)) { $args = [ 'in_footer' => $inFooter, 'strategy' => $strategy, ]; } wp_enqueue_script($handle, self::getSrc($src, $obfuscate, $pluginUrl), $deps, $version, $args); if ($localize) { $localize = apply_filters("wp_statistics_localize_{$handle}", $localize); wp_localize_script($handle, $object, $localize); } } /** * Localize a script. * * @param string $handle The script handle. * @param string $name The name of the object to be passed to the script. * @param array $data An array of data to be localized. * * @return void * @example Assets::localize('admin', 'foo', ['bar' => 'baz']); */ public static function localize($handle, $name, $data) { $handle = self::getHandle($handle); $object = self::getObject($name); $data = apply_filters("wp_statistics_localize_{$handle}", $data); wp_localize_script($handle, $object, $data); } /** * Register a script. * * @param string $handle The script handle. * @param string $src The source URL of the script. * @param array $deps An array of script dependencies. * @param string|null $version Optional. The version of the script. Defaults to plugin version. * @param bool $inFooter Whether to enqueue the script in the footer. * @param bool $obfuscate Ofuscate/Randomize asset's file name. * @param string $plugin_url The plugin URL. * * @return void * @example Assets::registerScript('chartjs', 'js/chart.min.js', [], '3.7.1', false, false, WP_STATISTICS_URL); */ public static function registerScript($handle, $src, $deps = [], $version = null, $inFooter = false, $obfuscate = false, $plugin_url = null) { // Get the handle for the script $handle = self::getHandle($handle); // Get the version of the script, if not provided, use the default version if ($version === null) { $version = WP_STATISTICS_VERSION; } // Register the script with WordPress wp_register_script($handle, self::getSrc($src, $obfuscate, $plugin_url), $deps, $version, $inFooter); } /** * Enqueue a style. * * @param string $handle The style handle. * @param string $src The source URL of the style. * @param array $deps An array of style dependencies. * @param string $media The context which style needs to be loaded: all, print, or screen * @param bool $obfuscate Ofuscate/Randomize asset's file name. * @param string $plugin_url The plugin URL. * @param string $version The version of the plugin. * * @return void * @example Assets::style('admin', 'dist/admin.css', ['jquery'], 'all', false, WP_STATISTICS_URL); */ public static function style($handle, $src, $deps = [], $media = 'all', $obfuscate = false, $plugin_url = null, $version = '') { $version = empty($version) ? WP_STATISTICS_VERSION : trim($version); wp_enqueue_style(self::getHandle($handle), self::getSrc($src, $obfuscate, $plugin_url), $deps, $version, $media); } /** * Get the handle for the script/style. * * @param string $handle The script/style handle. * @return string */ private static function getHandle($handle) { $handle = sprintf('wp-statistics-%s', strtolower($handle)); return apply_filters('wp_statistics_assets_handle', $handle); } /** * Get the source URL for the script/style. * * @param string $src The source URL. * @param bool $obfuscate Ofuscate/Randomize asset's file name. * @param string $plugin_url The plugin URL. * * @return string */ public static function getSrc($src, $obfuscate = false, $plugin_url = null) { if ($obfuscate) { $file = $plugin_url ? Helper::urlToDir($plugin_url) : self::$plugin_dir; $file = new AssetNameObfuscator(path_join($file, self::$asset_dir . '/' . $src)); return $file->getUrlThroughProxy(); } $url = $plugin_url ? untrailingslashit($plugin_url) . '/' : self::$plugin_url; return $url . self::$asset_dir . '/' . $src; } /** * Get the object name for script localization. * * @param string $handle The script handle. * @return string */ private static function getObject($handle) { $parts = explode('-', $handle); $camelCaseParts = array_map('ucfirst', $parts); return 'WP_Statistics_' . implode('_', $camelCaseParts) . '_Object'; } } DateRange.php 0000755 00000044457 15105501144 0007124 0 ustar 00 <?php namespace WP_Statistics\Components; use WP_STATISTICS\TimeZone; use WP_STATISTICS\User; use WP_STATISTICS\Helper; use WP_Statistics\Utils\Request; class DateRange { public static $defaultPeriod = '30days'; const USER_DATE_RANGE_META_KEY = 'wp_statistics_user_date_filter'; public static function validate($period) { if (empty($period)) { return false; } if (is_string($period) && !isset(self::getPeriods()[$period])) { return false; } if (is_array($period) && !isset($period['from'], $period['to'])) { return false; } return true; } /** * Stores the given date range in the user's meta data. * * @param array $range An array containing 'from' and 'to' date strings. */ public static function store($range) { $period = ''; $periods = self::getPeriods(); // If range is not set, or is invalid, use default if (!self::validate($range)) { $range = self::get(self::$defaultPeriod); } // If range is among the predefined periods, store the period key foreach ($periods as $key => $item) { if ($item['period']['from'] === $range['from'] && $item['period']['to'] === $range['to']) { $period = $key; break; } } // If it's custom range, store the range if (empty($period)) { $period = $range; } User::saveMeta(self::USER_DATE_RANGE_META_KEY, $period); } /** * Retrieves the period stored in the user's meta data, or from request object. * * @return string|array Could be a period name like '30days' or an array containing 'from' and 'to' date strings. */ public static function retrieve() { $result = []; $period = User::getMeta(self::USER_DATE_RANGE_META_KEY, true); if (!self::validate($period)) { $period = self::$defaultPeriod; } // Predefined date periods like '30days', 'this_month', etc... if (is_string($period)) { $periods = self::getPeriods(); $result = [ 'type' => 'period', 'value' => $period, 'range' => $periods[$period]['period'] ]; } // Custom date range store in usermeta like ['from' => '2024-01-01', 'to' => '2024-01-31'] if (is_array($period)) { $result = [ 'type' => 'custom', 'value' => $period, 'range' => $period ]; } // Manual date range from request object if (Request::has('from') && Request::has('to')) { $result = [ 'type' => 'manual', 'value' => Request::getParams(['from', 'to']), 'range' => Request::getParams(['from', 'to']) ]; } return $result; } /** * Gets a string and returns the specified date range. By default returns the stored period in usermeta. * * @param string|bool $name The name of the date range. By default false. * @param bool $excludeToday Whether to exclude today from the date range. Defaults to false. * @return array The date range. */ public static function get($period = false, $excludeToday = false) { $range = []; // If period is not provided, retrieve it if (!$period) { $storedRange = self::retrieve(); $range = $storedRange['range']; } else { $periods = self::getPeriods(); if (isset($periods[$period])) { $range = $periods[$period]['period']; } } // If period is 'total', set the from date to the initial post date if ($period === 'total') { $range['from'] = Helper::getInitialPostDate(); } if (!empty($range) && $excludeToday) { // Only applicable for periods that their last day is today, like 7days, 30days, etc... if ($period !== 'today' && self::compare($range['to'], 'is', 'today')) { $range['from'] = DateTime::subtract($range['from'], 1); $range['to'] = DateTime::subtract($range['to'], 1); } } return $range; } /** * Get the previous period based on a period name or custom date range. * By default it returns result based on the stored period in usermeta. * * @param mixed $period The name of the period (e.g., '30days', 'this_month') or custom date range. * @param bool $excludeToday Whether to exclude today from date calculations. Defaults to false. * @return array The previous period's date range. */ public static function getPrevPeriod($period = false, $excludeToday = false) { // If period is not provided, retrieve it if (!$period) { $period = self::retrieve(); $period = $period['value']; } // Check if the period name exists in the predefined periods $periods = self::getPeriods(); if (is_string($period) && isset($periods[$period])) { $currentRange = $periods[$period]['period']; $prevRange = $periods[$period]['prev_period']; // Only applicable for periods that their last day is today, like 7days, 30days, etc... if ($excludeToday && self::compare($currentRange['to'], '=', 'today')) { $prevRange['from'] = DateTime::subtract($prevRange['from'], 1); $prevRange['to'] = DateTime::subtract($prevRange['to'], 1); } return $prevRange; } // If it's a custom date range, calculate the previous period dynamically if (is_array($period)) { $range = self::resolveDate($period); $from = strtotime($range['from']); $to = strtotime($range['to']); $periodName = self::getPeriodFromRange($range); if ($periodName) { $currentRange = $periods[$periodName]['period']; $prevRange = $periods[$periodName]['prev_period']; // Only applicable for periods that their last day is today, like 7days, 30days, etc... if ($excludeToday && self::compare($currentRange['to'], 'is', 'today')) { $prevRange['from'] = DateTime::subtract($prevRange['from'], 1); $prevRange['to'] = DateTime::subtract($prevRange['to'], 1); } return $prevRange; } // Calculate the number of days in the current period $daysInPeriod = ($to - $from) / (60 * 60 * 24); // Calculate the previous period dates $prevTo = $from - 1; $prevFrom = $prevTo - $daysInPeriod * 60 * 60 * 24; return [ 'from' => date(DateTime::$defaultDateFormat, $prevFrom), 'to' => date(DateTime::$defaultDateFormat, $prevTo) ]; } // Fallback to default period if period is invalid return self::getPrevPeriod(self::$defaultPeriod); } /** * Retrieves the period name from a predefined date range. * * @param array $date An array containing 'from' and 'to' date strings. * @return string The period name. */ public static function getPeriodFromRange($date) { $periods = self::getPeriods(); foreach ($periods as $key => $period) { if ($date['from'] === $period['period']['from'] && $date['to'] === $period['period']['to']) { return $key; } } return false; } /** * Returns an array of predefined date periods. * * Each date period is represented as an array with two keys: 'period' and 'prev_period'. * The 'period' key represents the actual date period of the given string * The 'prev_period' key represents the date range before to the current date period. * * @return array An array of predefined date periods. */ public static function getPeriods() { return [ 'today' => [ 'period' => [ 'from' => DateTime::get(), 'to' => DateTime::get() ], 'prev_period' => [ 'from' => DateTime::get('-1 day'), 'to' => DateTime::get('-1 day') ], ], 'yesterday' => [ 'period' => [ 'from' => DateTime::get('-1 day'), 'to' => DateTime::get('-1 day'), ], 'prev_period' => [ 'from' => DateTime::get('-2 day'), 'to' => DateTime::get('-2 day') ], ], 'this_week' => [ 'period' => self::calculateWeekRange(0), 'prev_period' => self::calculateWeekRange(1) ], 'last_week' => [ 'period' => self::calculateWeekRange(1), 'prev_period' => self::calculateWeekRange(2) ], 'this_month' => [ 'period' => [ 'from' => DateTime::get('first day of this month'), 'to' => DateTime::get('last day of this month') ], 'prev_period' => [ 'from' => DateTime::get('first day of last month'), 'to' => DateTime::get('last day of last month') ] ], 'last_month' => [ 'period' => [ 'from' => DateTime::get('first day of -1 month'), 'to' => DateTime::get('last day of -1 month'), ], 'prev_period' => [ 'from' => DateTime::get('first day of -2 months'), 'to' => DateTime::get('last day of -2 months') ] ], '7days' => [ 'period' => [ 'from' => DateTime::get('-6 days'), 'to' => DateTime::get() ], 'prev_period' => [ 'from' => DateTime::get('-13 days'), 'to' => DateTime::get('-7 days') ] ], '14days' => [ 'period' => [ 'from' => DateTime::get('-13 days'), 'to' => DateTime::get() ], 'prev_period' => [ 'from' => DateTime::get('-27 days'), 'to' => DateTime::get('-14 days') ] ], '15days' => [ 'period' => [ 'from' => DateTime::get('-14 days'), 'to' => DateTime::get() ], 'prev_period' => [ 'from' => DateTime::get('-29 days'), 'to' => DateTime::get('-15 days') ] ], '28days' => [ 'period' => [ 'from' => DateTime::get('-27 days'), 'to' => DateTime::get() ], 'prev_period' => [ 'from' => DateTime::get('-55 days'), 'to' => DateTime::get('-28 days') ] ], '30days' => [ 'period' => [ 'from' => DateTime::get('-29 days'), 'to' => DateTime::get() ], 'prev_period' => [ 'from' => DateTime::get('-59 days'), 'to' => DateTime::get('-30 days') ] ], '90days' => [ 'period' => [ 'from' => DateTime::get('-89 days'), 'to' => DateTime::get() ], 'prev_period' => [ 'from' => DateTime::get('-179 days'), 'to' => DateTime::get('-90 days') ] ], '6months' => [ 'period' => [ 'from' => DateTime::get('-6 months'), 'to' => DateTime::get() ], 'prev_period' => [ 'from' => DateTime::get('-12 months'), 'to' => DateTime::get('-6 months') ] ], '12months' => [ 'period' => [ 'from' => DateTime::get('-12 months'), 'to' => DateTime::get() ], 'prev_period' => [ 'from' => DateTime::get('-24 months'), 'to' => DateTime::get('-12 months') ] ], 'this_year' => [ 'period' => [ 'from' => DateTime::get('now', 'Y-01-01'), 'to' => DateTime::get('now', 'Y-12-31') ], 'prev_period' => [ 'from' => DateTime::get('-1 year', 'Y-01-01'), 'to' => DateTime::get('-1 year', 'Y-12-31') ] ], 'last_year' => [ 'period' => [ 'from' => DateTime::get('-1 year', 'Y-01-01'), 'to' => DateTime::get('-1 year', 'Y-12-31') ], 'prev_period' => [ 'from' => DateTime::get('-2 year', 'Y-01-01'), 'to' => DateTime::get('-2 year', 'Y-12-31') ] ], 'total' => [ 'period' => [ 'from' => DateTime::get(0), 'to' => DateTime::get() ], 'prev_period' => [ 'from' => DateTime::get(0), 'to' => DateTime::get() ] ], ]; } /** * Get all dates within a specific range. * * @param array|string $range The date range, containing 'from' and 'to' keys, or a period name. * @return array An array of dates, each date represented as a Y-m-d string. */ public static function getDatesInRange($range) { $dates = []; $range = self::resolveDate($range); if (!is_array($range)) { return []; } $from = strtotime($range['from']); $to = strtotime($range['to']); while ($from <= $to) { $dates[] = date('Y-m-d', $from); $from += DAY_IN_SECONDS; } return $dates; } /** * Compare two dates. * * @param mixed $date1 A date string, array, or period name. * @param string $operator The operator to use for comparison. * @param mixed $date2 A date string, array, or period name. * * @return bool Whether the date ranges match the comparison operator. * @example * DateRange::compare($date, '=', 'today') * DateRange::compare($date, 'in', 'this_month') * DateRange::compare($date1, '!=', $date2) * DateRange::compare($date, 'in', ['from' => '2024-01-01', 'to' => '2024-01-31']) */ public static function compare($date1, $operator, $date2) { $range1 = self::resolveDate($date1); $range2 = self::resolveDate($date2); if (empty($range1) || empty($range2)) return false; $from1 = strtotime($range1['from']); $to1 = strtotime($range1['to']); $from2 = strtotime($range2['from']); $to2 = strtotime($range2['to']); switch ($operator) { case 'in': case 'between': return $from1 >= $from2 && $to1 <= $to2; case '<': return $to1 < $from2; case '<=': return $to1 <= $from2; case '>': return $from1 > $to2; case '>=': return $from1 >= $to2; case '!=': case 'not': return $from1 != $from2 || $to1 != $to2; case '=': case 'is': default: return $from1 == $from2 && $to1 == $to2; } } /** * Resolves the given date input to a 'from' and 'to' date array. * * @param mixed $date A date string, array, or period name. * @return array|bool An array containing 'from' and 'to' date strings. False if the date is invalid. */ public static function resolveDate($date) { // If date is an array if (is_array($date)) { if (isset($date['from'], $date['to'])) { return $date; } if (count($date) == 2) { $date = array_values($date); return ['from' => $date[0], 'to' => $date[1]]; } } // If date is a simple date string if (TimeZone::isValidDate($date)) { return ['from' => $date, 'to' => $date]; } // If date is a period name (string), get the range from the periods if (is_string($date)) { return self::get($date); } return false; } /** * Calculates the date range for a given week, where 0 is the current week, 1 is last week, 2 is two weeks ago, etc. * * @param int $weeksAgo The number of weeks ago to calculate the range for. * @return array An array containing 'from' and 'to' date strings representing the start and end of the week. */ private static function calculateWeekRange($weeksAgo = 0) { $startOfWeek = DateTime::getStartOfWeek('number'); $dateFormat = DateTime::$defaultDateFormat; $today = new \DateTime('now', DateTime::getTimezone()); $today->modify("-{$weeksAgo} weeks"); // Calculate the start of the week $weekStart = clone $today; $weekStart->modify(($startOfWeek - $weekStart->format('w') - 7) % 7 . ' days'); // Calculate the end of the week $weekEnd = clone $weekStart; $weekEnd->modify('+6 days'); return [ 'from' => $weekStart->format($dateFormat), 'to' => $weekEnd->format($dateFormat) ]; } } DateTime.php 0000755 00000014120 15105501144 0006746 0 ustar 00 <?php namespace WP_Statistics\Components; use ErrorException; class DateTime { public static $defaultDateFormat = 'Y-m-d'; public static $defaultTimeFormat = 'g:i a'; /** * Returns a formatted date string. * * @param string $date Human readable date string passed to strtotime() function. Defaults to 'now' * @param string $format The format string to use for the date. Default is 'Y-m-d'. * * @return string The formatted date string. */ public static function get($date = 'now', $format = 'Y-m-d') { if (is_numeric($date)) { $date = "@$date"; } $dateTime = new \DateTime($date, self::getTimezone()); return $dateTime->format($format); } /** * Returns the name of the day of the week used as the start of the week on the calendar. * * @param string $return Whether to return the name of the day, the number of the day, or both. * @return mixed */ public static function getStartOfWeek($return = 'name') { $dayNumber = intval(get_option('start_of_week', 0)); $weekDays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; $dayName = $weekDays[$dayNumber] ?? 'Monday'; // Return the name of the day, the number of the day, or both. switch ($return) { case 'number': return $dayNumber; case 'name': return $dayName; default: return ['number' => $dayNumber, 'name' => $dayName]; } } /** * Gets the date format string from WordPress settings. * * @return string */ public static function getDateFormat() { return get_option('date_format', self::$defaultDateFormat); } /** * Gets the time format string from WordPress settings. * * @return string */ public static function getTimeFormat() { return get_option('time_format', self::$defaultTimeFormat); } /** * Returns the timezone object based on the current WordPress setting. * * @return \DateTimeZone */ public static function getTimezone() { return new \DateTimeZone(wp_timezone_string()); } /** * Gets the date and time format string from WordPress settings. * * @param string $separator (optional) The separator to use between date and time. * @return string */ public static function getDateTimeFormat($separator = ' ') { return self::getDateFormat() . $separator . self::getTimeFormat(); } /** * Subtract a given number of days from a date string. * * @param string|int $date The date string to subtract from. * @param int $days The number of days to subtract. * @param string $format The format to use for the returned date string. Default 'Y-m-d'. * @return string The date string with the specified number of days subtracted. */ public static function subtract($date, $days, $format = 'Y-m-d') { return date($format, strtotime("-$days day", strtotime($date))); } /** * Formats a given date string according to WordPress settings and provided arguments. * * @param string|int $date The date string to format. If numeric, it is treated as a Unix timestamp. * @param array $args { * @type bool $include_time Whether to include the time in the formatted string. Default false. * @type bool $exclude_year Whether to exclude the year from the formatted string. Default false. * @type bool $short_month Whether to use a short month name (e.g. 'Jan' instead of 'January'). Default false. * @type string $separator The separator to use between date and time. Default ' '. * @type string $date_format The format string to use for the date. Default is the WordPress option 'date_format'. * @type string $time_format The format string to use for the time. Default is the WordPress option 'time_format'. * } * * @return string The formatted datetime string. * * @throws ErrorException If the provided datetime string is invalid. */ public static function format($date, $args = []) { $args = wp_parse_args($args, [ 'include_time' => false, 'exclude_year' => false, 'short_month' => false, 'separator' => ' ', 'date_format' => self::getDateFormat(), 'time_format' => self::getTimeFormat() ]); // If the date is numeric, treat it as a Unix timestamp if (is_numeric($date)) { $dateTime = new \DateTime('@' . $date, new \DateTimeZone('UTC')); $dateTime->setTimezone(self::getTimezone()); } else { $dateTime = new \DateTime($date, self::getTimezone()); } $format = $args['date_format']; if ($args['include_time'] === true) { $format = $args['date_format'] . $args['separator'] . $args['time_format']; } if ($args['exclude_year']) { $format = preg_replace('/(,\s?Y|Y\s?,|Y[, \/-]?|[, \/-]?Y)/i', '', $format); } if ($args['short_month']) { $format = str_replace('F', 'M', $format); } return $dateTime->format($format); } /** * Check is Valid date * * @param $date * @return bool */ public static function isValidDate($date) { if (empty($date)) { return false; } if (preg_match("/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/", $date) && strtotime($date) !== false) { return true; } return false; } /** * Checks if the given date is today or in the future. * * @param string $date * * @return bool */ public static function isTodayOrFutureDate($date) { $today = date('Y-m-d'); if (!$date || strtotime($date) === false) { return false; } $inputDate = date('Y-m-d', strtotime($date)); return ($inputDate >= $today); } } Encryptor.php 0000755 00000006634 15105501144 0007252 0 ustar 00 <?php namespace WP_Statistics\Components; use WP_Statistics; class Encryptor { private const NONCE_BYTES = SODIUM_CRYPTO_SECRETBOX_NONCEBYTES; private const KEY_BYTES = SODIUM_CRYPTO_SECRETBOX_KEYBYTES; private const CIPHER_KEY_OPTION = 'wp_statistics_cipher_key'; // Cached key to be used for encryption/decryption. private static $key = null; /** * Retrieve the key used for encryption/decryption. * * If defined, SALTs are used to generate a key. * If not, a random key is generated and stored in wp_options. * * @return string */ private static function key(): string { if (self::$key !== null) { return self::$key; } // Build material from whichever SALTs are defined. $material = (defined('AUTH_KEY') ? AUTH_KEY : '') . (defined('SECURE_AUTH_KEY') ? SECURE_AUTH_KEY : '') . (defined('LOGGED_IN_KEY') ? LOGGED_IN_KEY : '') . (defined('NONCE_KEY') ? NONCE_KEY : '') . (defined('AUTH_SALT') ? AUTH_SALT : '') . (defined('SECURE_AUTH_SALT') ? SECURE_AUTH_SALT : '') . (defined('LOGGED_IN_SALT') ? LOGGED_IN_SALT : '') . (defined('NONCE_SALT') ? NONCE_SALT : ''); if ($material !== '') { self::$key = sodium_crypto_generichash($material, '', self::KEY_BYTES); return self::$key; } // If no SALTs are defined, fall back to a persistent random key in wp_options. $cipherKey = get_option(self::CIPHER_KEY_OPTION); $rawKey = is_string($cipherKey) ? base64_decode($cipherKey, true) : false; if ($rawKey == false || strlen($rawKey) !== self::KEY_BYTES) { $rawKey = random_bytes(self::KEY_BYTES); update_option(self::CIPHER_KEY_OPTION, base64_encode($rawKey), true); } // Cache the key for future use. self::$key = $rawKey; return self::$key; } /** * Encrypts a given plain text string and returns the encrypted token. * * @param string $plainText * @return string The encrypted token. */ public static function encrypt($plainText) { $nonce = random_bytes(self::NONCE_BYTES); $cipher = sodium_crypto_secretbox($plainText, $nonce, self::key()); return rtrim(strtr(base64_encode($nonce . $cipher), '+/', '-_'), '='); } /** * Decrypts a given encrypted token and returns the plain text string. * * @param string $token The encrypted token to decrypt. * @return string|false The decrypted plain text string, or false if decryption failed. */ public static function decrypt($token) { $token = base64_decode(strtr($token, '-_', '+/')); if ($token === false || strlen($token) < self::NONCE_BYTES) { WP_Statistics::log(esc_html__('Malformed token', 'wp-statistics'), 'error'); return false; } $nonce = substr($token, 0, self::NONCE_BYTES); $cipher = substr($token, self::NONCE_BYTES); $plain = sodium_crypto_secretbox_open($cipher, $nonce, self::key()); if ($plain === false) { WP_Statistics::log(esc_html__('Failed to decrypt token', 'wp-statistics'), 'error'); return false; } return $plain; } } Event.php 0000755 00000005267 15105501144 0006347 0 ustar 00 <?php namespace WP_Statistics\Components; use WP_STATISTICS\Schedule; class Event { /** * Get a scheduled event. * * @param string $event The action hook of the event. * @return object|false The event object if found, false otherwise. */ public static function get($event) { return wp_get_scheduled_event($event); } /** * Schedules a WordPress event hook if it is not already scheduled. * * @param string $hook The action hook of the event. * @param int $timestamp The timestamp for when the event should occur. * @param string $recurrence How often the event should be repeated. * @param mixed $callback The callback of the event. * * @return void */ public static function schedule($event, $timestamp, $recurrence, $callback = null) { if (!self::isScheduled($event)) { wp_schedule_event($timestamp, $recurrence, $event); } if ($callback) { add_action($event, $callback); } } /** * Unschedules a WordPress event hook, if it is scheduled. * * @param string $event The action hook of the event. * * @return void */ public static function unschedule($event) { if (self::isScheduled($event)) { wp_unschedule_event(wp_next_scheduled($event), $event); } } /** * Checks if a WordPress event hook is scheduled. * * @param string $event The action hook of the event. * @return bool True if the event is scheduled, false otherwise. */ public static function isScheduled($event) { return wp_next_scheduled($event) ? true : false; } /** * Reschedule an already scheduled event hook. * * @param string $event * @param string $recurrence * @param string $nextRun If it's not provided, use the next scheduled time based on recurrence * * @return void */ public static function reschedule($event, $recurrence, $nextRun = null) { // If not scheduled, return if (!self::isScheduled($event)) return; // If already scheduled with the same recurrence and next run, return $prevEvent = self::get($event); if ($prevEvent->schedule === $recurrence && $prevEvent->timestamp === $nextRun) return; // unschedule previous event self::unschedule($event); $schedules = Schedule::getSchedules(); if (isset($schedules[$recurrence])) { if (!$nextRun) { $nextRun = $schedules[$recurrence]['next_schedule']; } self::schedule($event, $nextRun, $recurrence); } } } RemoteRequest.php 0000755 00000014243 15105501144 0010064 0 ustar 00 <?php namespace WP_Statistics\Components; use Exception; use WP_Statistics\Traits\TransientCacheTrait; class RemoteRequest { use TransientCacheTrait; public $requestUrl; private $parsedArgs = []; /** * Response body from the request. * @var string|null */ private $responseCode; /** * Response body from the request. * @var string|null */ private $responseBody; /** * Complete WordPress HTTP API response object * @var array|null */ private $response; /** * @param string $url * @param string $method * @param array $params URL parameters. * @param array $args Request arguments. */ public function __construct($url, $method = 'GET', $params = [], $args = []) { // Filter to modify URL parameters $params = apply_filters('wp_statistics_remote_request_params', $params); // Build request URL $this->requestUrl = add_query_arg($params, $url); // Filter to modify arguments $args = apply_filters('wp_statistics_remote_request_args', $args); // Prepare the arguments $this->parsedArgs = wp_parse_args($args, [ 'method' => $method, 'timeout' => 10, ]); } /** * Returns request URL. * * @return string */ public function getRequestUrl() { return $this->requestUrl; } /** * Returns parsed request arguments. * * @return array */ public function getParsedArgs() { return $this->parsedArgs; } /** * Generates a cache key based on the request URL and arguments. * * @return string */ public function generateCacheKey() { return wp_json_encode(array_merge(['url' => $this->requestUrl], $this->parsedArgs)); } /** * Clears the cache for the current request. * * This method is useful when you want to make sure that the next request is not served from the cache. */ public function clearCache() { $this->clearCachedResult($this->generateCacheKey()); } /** * Checks if the given HTTP response code indicates a successful request. * * @return bool True if the response code indicates a successful request, false otherwise. */ public function isRequestSuccessful() { return in_array($this->responseCode, [200, 201, 202]); } /** * Checks if the request is cached. * * @return bool True if the request is cached, false otherwise. */ public function isCached() { return $this->getCachedResult($this->generateCacheKey()) !== false; } /** * Executes the request with optional caching. * * @param bool $throwFailedHttpCodeResponse Whether or not to throw an exception if the request returns a failed HTTP code. * @param bool $useCache Whether or not to use caching. * @param int $cacheExpiration Cache expiration time in seconds. * * @return mixed * * @throws Exception */ public function execute($throwFailedHttpCodeResponse = true, $useCache = true, $cacheExpiration = HOUR_IN_SECONDS) { // Generate the cache key $cacheKey = $this->generateCacheKey(); // Check if cached result exists if caching is enabled if ($useCache) { $cachedResponse = $this->getCachedResult($cacheKey); if ($cachedResponse !== false) { return $cachedResponse; } } // Execute the request if no cached result exists or caching is disabled $response = wp_remote_request( $this->requestUrl, $this->parsedArgs ); $this->response = $response; if (is_wp_error($response)) { if (empty($throwFailedHttpCodeResponse)) { return false; } throw new Exception(esc_html($response->get_error_message())); } $this->responseCode = wp_remote_retrieve_response_code($response); $this->responseBody = wp_remote_retrieve_body($response); if ($throwFailedHttpCodeResponse && !$this->isRequestSuccessful()) { throw new Exception(sprintf( esc_html__('Failed to get success response. URL: %s, Method: %s, Status Code: %s', 'wp-statistics'), esc_html($this->requestUrl), esc_html($this->parsedArgs['method'] ?? 'GET'), esc_html($this->responseCode) )); } $responseJson = json_decode($this->responseBody); // Cache the result if caching is enabled $resultToCache = ($responseJson === null) ? $this->responseBody : $responseJson; if ($useCache) { if ($this->isRequestSuccessful() && (is_object($resultToCache) || is_array($resultToCache))) { $this->setCachedResult($cacheKey, $resultToCache, $cacheExpiration); } } return $resultToCache; } /** * Returns the response body from the executed request * * @return string|null The response body or null if no request has been executed */ public function getResponseBody() { return $this->responseBody; } /** * Returns the HTTP response code from the last executed request * * @return int|null The HTTP response code or null if no request has been executed */ public function getResponseCode() { return $this->responseCode; } /** * Retrieves the complete WordPress HTTP API response object * * @return array|null Complete response array or null if no request executed */ public function getResponse() { return $this->response; } /** * Validate if response is a valid JSON array * * @return bool Returns true if valid JSON array, false otherwise */ public function isValidJsonResponse() { if ( !empty($this->responseBody) && is_string($this->responseBody) && is_array(json_decode($this->responseBody, true)) && json_last_error() == 0 ) { return true; } return false; } } Singleton.php 0000755 00000001132 15105501144 0007213 0 ustar 00 <?php namespace WP_Statistics\Components; /** * Simple singleton that we will extend */ class Singleton { /** * @var Singleton[] $instance Instance */ private static $instances = []; /** * Construct */ private function __construct() { } /** * Get Instance * * @return Singleton Instance */ public static function instance() { $class = static::class; if (!isset(self::$instances[$class])) { self::$instances[$class] = new static(); } return self::$instances[$class]; } } SystemCleaner.php 0000755 00000001511 15105501144 0010030 0 ustar 00 <?php namespace WP_Statistics\Components; use WP_Statistics\Utils\Query; use WP_Statistics\Service\Admin\LicenseManagement\ApiEndpoints; class SystemCleaner { /** * Clears a specific cache. * * @param string $cacheId */ public static function clearTransientById($cacheId) { return delete_transient("wp_statistics_cache_$cacheId"); } /** * Clears all caches. */ public static function clearAllTransients() { return Query::delete('options') ->where('option_name', 'LIKE', '%wp_statistics_cache%') ->execute(); } /** * Clears remote add-ons list cache. */ public static function clearAddonsListCache() { $request = new RemoteRequest(ApiEndpoints::PRODUCT_LIST, 'GET'); $request->clearCache(); } } View.php 0000755 00000003043 15105501144 0006166 0 ustar 00 <?php namespace WP_Statistics\Components; use WP_Statistics\Exception\SystemErrorException; class View { /** * Load a view file and pass data to it. * * @param string|array $view The view path inside views directory * @param array $args An associative array of data to pass to the view. * @param bool $return Return the template if requested * @param string $baseDir The base directory to load the view, defaults to WP_STATISTICS_DIR * * @throws Exception if the view file cannot be found. */ public static function load($view, $args = [], $return = false, $baseDir = null) { // Default to WP_STATISTICS_DIR $baseDir = empty($baseDir) ? WP_STATISTICS_DIR : $baseDir; try { $viewList = is_array($view) ? $view : [$view]; foreach ($viewList as $view) { $viewPath = "$baseDir/views/$view.php"; if (!file_exists($viewPath)) { throw new SystemErrorException(esc_html__("View file not found: {$viewPath}", 'wp-statistics')); } if (!empty($args)) { extract($args); } // Return the template if requested if ($return) { ob_start(); include $viewPath; return ob_get_clean(); } include $viewPath; } } catch (\Exception $e) { \WP_Statistics::log($e->getMessage(), 'error'); } } }
| ver. 1.4 |
Github
|
.
| PHP 8.1.33 | Генерация страницы: 0.04 |
proxy
|
phpinfo
|
Настройка