557 lines
16 KiB
PHP
557 lines
16 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Utility class used for various task
|
|
*
|
|
* Standard: PSR-2
|
|
*
|
|
* @link http://www.php-fig.org/psr/psr-2
|
|
*
|
|
* @package DUP_PRO
|
|
* @subpackage classes/utilities
|
|
* @copyright (c) 2017, Snapcreek LLC
|
|
* @license https://opensource.org/licenses/GPL-3.0 GNU Public License
|
|
*/
|
|
|
|
defined("ABSPATH") or die("");
|
|
|
|
use Duplicator\Libs\Shell\Shell;
|
|
|
|
class DUP_PRO_U
|
|
{
|
|
/**
|
|
* return absolute path for the directories that are core directories
|
|
*
|
|
* @param bool $original If true it returns yes the original realpaths and paths, in case they are links, Otherwise it returns only the realpaths.
|
|
*
|
|
* @return string[]
|
|
*/
|
|
public static function getWPCoreDirs($original = false)
|
|
{
|
|
$corePaths = DUP_PRO_Archive::getArchiveListPaths();
|
|
$corePaths[] = $corePaths['abs'] . '/wp-admin';
|
|
$corePaths[] = $corePaths['abs'] . '/wp-includes';
|
|
|
|
if ($original) {
|
|
$origPaths = DUP_PRO_Archive::getOriginalPaths();
|
|
$origPaths[] = $origPaths['abs'] . '/wp-admin';
|
|
$origPaths[] = $origPaths['abs'] . '/wp-includes';
|
|
|
|
$corePaths = array_merge($corePaths, $origPaths);
|
|
}
|
|
|
|
return array_values(array_unique($corePaths));
|
|
}
|
|
|
|
/**
|
|
* return absolute path for the files that are core directories
|
|
*
|
|
* @return string[]
|
|
*/
|
|
public static function getWPCoreFiles()
|
|
{
|
|
return array(DUP_PRO_Archive::getArchiveListPaths('wpconfig') . '/wp-config.php');
|
|
}
|
|
|
|
/**
|
|
* Converts an absolute path to a relative path
|
|
*
|
|
* @param string $from The the path relative to $to
|
|
* @param string $to The full path of the directory to transform
|
|
*
|
|
* @return string A string of the result
|
|
*/
|
|
public static function getRelativePath($from, $to)
|
|
{
|
|
// some compatibility fixes for Windows paths
|
|
$from = is_dir($from) ? rtrim($from, '\/') . '/' : $from;
|
|
$to = is_dir($to) ? rtrim($to, '\/') . '/' : $to;
|
|
$from = str_replace('\\', '/', $from);
|
|
$to = str_replace('\\', '/', $to);
|
|
|
|
$from = explode('/', $from);
|
|
$to = explode('/', $to);
|
|
$relPath = $to;
|
|
|
|
foreach ($from as $depth => $dir) {
|
|
// find first non-matching dir
|
|
if ($dir === $to[$depth]) {
|
|
// ignore this directory
|
|
array_shift($relPath);
|
|
} else {
|
|
// get number of remaining dirs to $from
|
|
$remaining = count($from) - $depth;
|
|
if ($remaining > 1) {
|
|
// add traversals up to first matching dir
|
|
$padLength = (count($relPath) + $remaining - 1) * -1;
|
|
$relPath = array_pad($relPath, $padLength, '..');
|
|
break;
|
|
} else {
|
|
//$relPath[0] = './' . $relPath[0];
|
|
}
|
|
}
|
|
}
|
|
return implode('/', $relPath);
|
|
}
|
|
|
|
/**
|
|
* Gets the percentage of one value to another
|
|
* example:
|
|
* $val1 = 100
|
|
* $val2 = 400
|
|
* $res = 25
|
|
*
|
|
* @param int|float $val1 The value to calculate the percentage
|
|
* @param int|float $val2 The total value to calculate the percentage against
|
|
* @param int $precision The number of decimal places to round to
|
|
*
|
|
* @return float Returns the results
|
|
*/
|
|
public static function percentage($val1, $val2, $precision = 0)
|
|
{
|
|
$division = $val1 / (float) $val2;
|
|
$res = $division * 100;
|
|
return round($res, $precision);
|
|
}
|
|
|
|
/**
|
|
* Display human readable byte sizes
|
|
*
|
|
* @param int $size The size in bytes
|
|
*
|
|
* @return string The size of bytes readable such as 100KB, 20MB, 1GB etc.
|
|
*/
|
|
public static function byteSize($size)
|
|
{
|
|
try {
|
|
$size = (int) $size;
|
|
$units = array(
|
|
'B',
|
|
'KB',
|
|
'MB',
|
|
'GB',
|
|
'TB',
|
|
);
|
|
for ($i = 0; $size >= 1024 && $i < 4; $i++) {
|
|
$size /= 1024;
|
|
}
|
|
return round($size, 2) . $units[$i];
|
|
} catch (Exception $e) {
|
|
return "n/a";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return a string with the elapsed time in seconds
|
|
*
|
|
* @see getMicrotime()
|
|
*
|
|
* @param int|float $end The final time in the sequence to measure
|
|
* @param int|float $start The start time in the sequence to measure
|
|
*
|
|
* @return string The time elapsed from $start to $end as 5.89 sec.
|
|
*/
|
|
public static function elapsedTime($end, $start)
|
|
{
|
|
|
|
return sprintf('%.3f sec.', abs($end - $start));
|
|
}
|
|
|
|
/**
|
|
* Return a float with the elapsed time in seconds
|
|
*
|
|
* @see getMicrotime(), elapsedTime()
|
|
*
|
|
* @param int|float $end The final time in the sequence to measure
|
|
* @param int|float $start The start time in the sequence to measure
|
|
*
|
|
* @return string The time elapsed from $start to $end as 5.89
|
|
*/
|
|
public static function elapsedTimeU($end, $start)
|
|
{
|
|
return sprintf('%.3f', abs($end - $start));
|
|
}
|
|
|
|
/**
|
|
* Gets the contents of the file as an attachment type
|
|
*
|
|
* @param string $filepath The full path the file to read
|
|
* @param string $contentType The header content type to force when pushing the attachment
|
|
*
|
|
* @return void
|
|
*/
|
|
public static function getDownloadAttachment($filepath, $contentType)
|
|
{
|
|
// Clean previous or after eny notice texts
|
|
ob_clean();
|
|
ob_start();
|
|
$filename = basename($filepath);
|
|
|
|
header("Content-Type: {$contentType}");
|
|
header("Content-Disposition: attachment; filename={$filename}");
|
|
header("Pragma: public");
|
|
|
|
if (readfile($filepath) === false) {
|
|
$msg = sprintf(__('Couldn\'t read %s', 'duplicator-pro'), $filepath);
|
|
throw new Exception($msg);
|
|
}
|
|
ob_end_flush();
|
|
}
|
|
|
|
/**
|
|
* Return the path of an executable program
|
|
*
|
|
* @param string $exeFilename A file name or path to a file name of the executable
|
|
*
|
|
* @return string|null Returns the full path of the executable or null if not found
|
|
*/
|
|
public static function getExeFilepath($exeFilename)
|
|
{
|
|
$filepath = null;
|
|
|
|
if (!Shell::test()) {
|
|
return null;
|
|
}
|
|
|
|
$shellOutput = Shell::runCommand("hash $exeFilename 2>&1", Shell::AVAILABLE_COMMANDS);
|
|
if ($shellOutput !== false && $shellOutput->isEmpty()) {
|
|
$filepath = $exeFilename;
|
|
} else {
|
|
$possible_paths = array(
|
|
"/usr/bin/$exeFilename",
|
|
"/opt/local/bin/$exeFilename",
|
|
);
|
|
|
|
foreach ($possible_paths as $path) {
|
|
if (@file_exists($path)) {
|
|
$filepath = $path;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $filepath;
|
|
}
|
|
|
|
/**
|
|
* Get current microtime as a float. Method is used for simple profiling
|
|
*
|
|
* @see elapsedTime
|
|
*
|
|
* @return float A float in the form "msec sec", where sec is the number of seconds since the Unix epoch
|
|
*/
|
|
public static function getMicrotime()
|
|
{
|
|
return microtime(true);
|
|
}
|
|
|
|
/**
|
|
* Gets an SQL lock request
|
|
*
|
|
* @see releaseSqlLock()
|
|
*
|
|
* @param string $lock_name The name of the lock to check
|
|
*
|
|
* @return bool Returns true if an SQL lock request was successful
|
|
*/
|
|
public static function getSqlLock($lock_name = 'duplicator_pro_lock')
|
|
{
|
|
global $wpdb;
|
|
|
|
$query_string = "select GET_LOCK('{$lock_name}', 0)";
|
|
|
|
$ret_val = $wpdb->get_var($query_string);
|
|
|
|
if ($ret_val == 0) {
|
|
DUP_PRO_Log::trace("Mysql lock {$lock_name} denied");
|
|
return false;
|
|
} elseif ($ret_val == null) {
|
|
DUP_PRO_Log::trace("Error retrieving mysql lock {$lock_name}");
|
|
return false;
|
|
}
|
|
|
|
DUP_PRO_Log::trace("Mysql lock {$lock_name} acquired");
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Gets an SQL lock request
|
|
*
|
|
* @see releaseSqlLock()
|
|
*
|
|
* @param string $lock_name The name of the lock to check
|
|
*
|
|
* @return bool Returns true if an SQL lock request was successful
|
|
*/
|
|
public static function isSqlLockLocked($lock_name = 'duplicator_pro_lock')
|
|
{
|
|
global $wpdb;
|
|
|
|
$query_string = "select IS_FREE_LOCK('{$lock_name}')";
|
|
|
|
$ret_val = $wpdb->get_var($query_string);
|
|
|
|
if ($ret_val == 0) {
|
|
DUP_PRO_Log::trace("MySQL lock {$lock_name} is in use");
|
|
return true;
|
|
} elseif ($ret_val == null) {
|
|
DUP_PRO_Log::trace("Error retrieving mysql lock {$lock_name}");
|
|
return false;
|
|
} else {
|
|
DUP_PRO_Log::trace("MySQL lock {$lock_name} is free");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Verifies that a correct security nonce was used. If correct nonce is not used, It will cause to die
|
|
*
|
|
* A nonce is valid for 24 hours (by default).
|
|
*
|
|
* @param string $nonce Nonce value that was used for verification, usually via a form field.
|
|
* @param string|int $action Should give context to what is taking place and be the same when nonce was created.
|
|
*
|
|
* @return void
|
|
*/
|
|
public static function verifyNonce($nonce, $action)
|
|
{
|
|
if (!wp_verify_nonce($nonce, $action)) {
|
|
die('Security issue');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Does the current user have the capability
|
|
* Dies if user doesn't have the correct capability
|
|
*
|
|
* @return void
|
|
*/
|
|
public static function checkAjax()
|
|
{
|
|
if (!wp_doing_ajax()) {
|
|
$errorMsg = esc_html__('You do not have called from AJAX to access this page.', 'duplicator-pro');
|
|
DUP_PRO_Log::trace($errorMsg);
|
|
error_log($errorMsg);
|
|
wp_die(esc_html($errorMsg));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Rturn true if sql lock is set
|
|
*
|
|
* @param string $lock_name lock nam
|
|
*
|
|
* @return bool
|
|
*/
|
|
public static function checkSqlLock($lock_name = 'duplicator_pro_lock')
|
|
{
|
|
global $wpdb;
|
|
|
|
$query_string = "SELECT IS_USED_LOCK('{$lock_name}')";
|
|
$ret_val = $wpdb->get_var($query_string);
|
|
|
|
return $ret_val > 0;
|
|
}
|
|
|
|
/**
|
|
* Releases the SQL lock request
|
|
*
|
|
* @see getSqlLock()
|
|
*
|
|
* @param string $lock_name The name of the lock to release
|
|
*
|
|
* @return void
|
|
*/
|
|
public static function releaseSqlLock($lock_name = 'duplicator_pro_lock')
|
|
{
|
|
global $wpdb;
|
|
|
|
$query_string = "select RELEASE_LOCK('{$lock_name}')";
|
|
$ret_val = $wpdb->get_var($query_string);
|
|
|
|
if ($ret_val == 0) {
|
|
DUP_PRO_Log::trace("Failed releasing sql lock {$lock_name} because it wasn't established by this thread");
|
|
} elseif ($ret_val == null) {
|
|
DUP_PRO_Log::trace("Tried to release sql lock {$lock_name} but it didn't exist");
|
|
} else {
|
|
// Lock was released
|
|
DUP_PRO_Log::trace("SQL lock {$lock_name} released");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets a value or returns a default
|
|
*
|
|
* @param mixed $val The value to set
|
|
* @param mixed $default The value to default to if the val is not set
|
|
*
|
|
* @return mixed A value or a default
|
|
*/
|
|
public static function setVal($val, $default = null)
|
|
{
|
|
return isset($val) ? $val : $default;
|
|
}
|
|
|
|
/**
|
|
* Check is set and not empty, sets a value or returns a default
|
|
*
|
|
* @param mixed $val The value to set
|
|
* @param mixed $default The value to default to if the val is not set
|
|
*
|
|
* @return mixed A value or a default
|
|
*/
|
|
public static function isEmpty($val, $default = null)
|
|
{
|
|
return isset($val) && !empty($val) ? $val : $default;
|
|
}
|
|
|
|
/**
|
|
* Returns the last N lines of a file. Simular to tail command
|
|
*
|
|
* @param string $filepath The full path to the file to be tailed
|
|
* @param int $lines The number of lines to return with each tail call
|
|
*
|
|
* @return false|string The last N parts of the file, false on failure
|
|
*/
|
|
public static function tailFile($filepath, $lines = 2)
|
|
{
|
|
// Open file
|
|
$f = @fopen($filepath, "rb");
|
|
if ($f === false) {
|
|
return false;
|
|
}
|
|
|
|
// Sets buffer size
|
|
$buffer = 256;
|
|
|
|
// Jump to last character
|
|
fseek($f, -1, SEEK_END);
|
|
|
|
// Read it and adjust line number if necessary
|
|
// (Otherwise the result would be wrong if file doesn't end with a blank line)
|
|
if (fread($f, 1) != "\n") {
|
|
$lines -= 1;
|
|
}
|
|
|
|
// Start reading
|
|
$output = '';
|
|
$chunk = '';
|
|
|
|
// While we would like more
|
|
while (ftell($f) > 0 && $lines >= 0) {
|
|
// Figure out how far back we should jump
|
|
$seek = min(ftell($f), $buffer);
|
|
// Do the jump (backwards, relative to where we are)
|
|
fseek($f, -$seek, SEEK_CUR);
|
|
// Read a chunk and prepend it to our output
|
|
$output = ($chunk = fread($f, $seek)) . $output;
|
|
// Jump back to where we started reading
|
|
fseek($f, -mb_strlen($chunk, '8bit'), SEEK_CUR);
|
|
// Decrease our line counter
|
|
$lines -= substr_count($chunk, "\n");
|
|
}
|
|
|
|
// While we have too many lines
|
|
// (Because of buffer size we might have read too many)
|
|
while ($lines++ < 0) {
|
|
// Find first newline and remove all text before that
|
|
$output = substr($output, strpos($output, "\n") + 1);
|
|
}
|
|
fclose($f);
|
|
return trim($output);
|
|
}
|
|
|
|
/**
|
|
* Check given table is exist in real
|
|
*
|
|
* @param string $table string Table name
|
|
*
|
|
* @return bool
|
|
*/
|
|
public static function isTableExists($table)
|
|
{
|
|
// It will clear the $GLOBALS['wpdb']->last_error var
|
|
$GLOBALS['wpdb']->flush();
|
|
$sql = "SELECT 1 FROM `" . esc_sql($table) . "` LIMIT 1;";
|
|
$ret = $GLOBALS['wpdb']->get_var($sql);
|
|
if (empty($GLOBALS['wpdb']->last_error)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Finds if its a valid executable or not
|
|
*
|
|
* @param string $cmd A non zero length executable path to find if that is executable or not.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public static function isExecutable($cmd)
|
|
{
|
|
if (strlen($cmd) == 0) {
|
|
return false;
|
|
}
|
|
|
|
if (
|
|
@is_executable($cmd)
|
|
|| !Shell::runCommand($cmd, Shell::AVAILABLE_COMMANDS)->isEmpty()
|
|
|| !Shell::runCommand($cmd . ' -?', Shell::AVAILABLE_COMMANDS)->isEmpty()
|
|
) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Look into string and try to fix its natural expected value type
|
|
*
|
|
* @param mixed $data Simple string
|
|
*
|
|
* @return mixed value with it's natural string type
|
|
*/
|
|
public static function valType($data)
|
|
{
|
|
if (is_string($data)) {
|
|
if (is_numeric($data)) {
|
|
if ((int) $data == $data) {
|
|
return (int) $data;
|
|
} elseif ((float) $data == $data) {
|
|
return (float) $data;
|
|
}
|
|
} elseif (in_array(strtolower($data), array('true', 'false'), true)) {
|
|
return ($data == 'true');
|
|
}
|
|
} elseif (is_array($data)) {
|
|
foreach ($data as $key => $str) {
|
|
$data[$key] = DUP_PRO_U::valType($str);
|
|
}
|
|
}
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Check given var is curl resource or instance of CurlHandle or CurlMultiHandle
|
|
* It is used for check curl_init() return, because
|
|
* curl_init() returns resource in lower PHP version than 8.0
|
|
* curl_init() returns class instance in PHP version 8.0
|
|
* Ref. https://php.watch/versions/8.0/resource-CurlHandle
|
|
*
|
|
* @param resource|object $var var to check
|
|
*
|
|
* @return boolean
|
|
*/
|
|
public static function isCurlResourceOrInstance($var)
|
|
{
|
|
// CurlHandle class instance return of curl_init() in php 8.0
|
|
// CurlMultiHandle class instance return of curl_multi_init() in php 8.0
|
|
|
|
if (is_resource($var) || ($var instanceof CurlHandle) || ($var instanceof CurlMultiHandle)) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
}
|