File: D:/HostingSpaces/SBogers10/zelfverkopen.komma.pro/kommaterug.php
<?php
/**
* Class KommaTerug
*
* Backup script for any website
*/
Class KommaTerug
{
/** Terminal colors */
const COLOR_BLACK = '0;30';
const COLOR_BLUE = '0;34';
const COLOR_GREEN = '0;32';
const COLOR_CYAN = '0;36';
const COLOR_RED = '0;31';
const COLOR_PURPLE = '0;35';
const COLOR_BROWN = '0;33';
const COLOR_LIGHT_GRAY = '0;37';
const COLOR_DARK_GRAY = '1;30';
const COLOR_LIGHT_BLUE = '1;34';
const COLOR_LIGHT_GREEN = '1;32';
const COLOR_LIGHT_CYAN = '1;36';
const COLOR_LIGHT_RED = '1;31';
const COLOR_LIGHT_PURPLE = '1;35';
const COLOR_YELLOW = '1;33';
const COLOR_WHITE = '1;37';
/** Script version number. */
const version = 1;
/** @var string $folder The working folder */
private static $folder = __DIR__.DIRECTORY_SEPARATOR.'kommaterug' . DIRECTORY_SEPARATOR;
/** @var string $logFolderName */
private static $logFolderName = 'logs' . DIRECTORY_SEPARATOR;
/** @var string $scriptConfigFileName The config file name for the script */
private static $scriptConfigFileName = 'config.php';
/** @var string $projectConfigFileName The config file name for the project that you want to backup */
private $projectConfigFileName;
/** @var int $maxBackupsSize in bytes */
private $maxBackupsSize;
/** @var PDO $PDORead used as a database abstraction layer */
private $PDORead;
/** @var PDO $PDORead used as a database abstraction layer */
private $PDOWrite;
/** @var string $dbHostRead */
private $dbHostRead;
/** @var string $dbPortRead */
private $dbPortRead;
/** @var string $dbDatabaseRead */
private $dbDatabaseRead;
/** @var string $dbUsernameRead */
private $dbUsernameRead;
/** @var string $dbPasswordRead */
private $dbPasswordRead;
/** @var string $dbHostWrite */
private $dbHostWrite;
/** @var string $dbPortWrite */
private $dbPortWrite;
/** @var string $dbDatabaseWrite */
private $dbDatabaseWrite;
/** @var string $dbUsernameWrite */
private $dbUsernameWrite;
/** @var string $dbPasswordWrite */
private $dbPasswordWrite;
/** @var array $config */
private $config;
/** @var bool $constructedSuccessfully */
private $constructedSuccessfully;
/**
* KommaTerug constructor.
*/
public function __construct()
{
self::sendHeadersToPreventCaching();
$this->projectConfigFileName = '.env';
$this->maxBackupsSize = 3500000000; //Approx 3.5 GB
if (!self::initializeFilesAndFolders()) {
$this->constructedSuccessfully = false;
return false;
}
if (!$this->parseScriptConfigFile()) {
$this->constructedSuccessfully = false;
return false;
}
$this->constructedSuccessfully = true;
}
/**
* KommaTerug destructor
*/
public function __destruct()
{
if(!$this->constructedSuccessfully) return false;
if ($this->PDORead) {
$this->PDORead = null;
}
if ($this->PDOWrite) {
$this->PDOWrite = null;
}
$this->cleanup();
}
/**
* With this command you run the app. This is where it all starts after the instantiation.
* Called manually
*/
public function run()
{
if(!$this->constructedSuccessfully) return false;
self::handleExceptionsIfAny(function () {
if ($this->runsFromCommandLine()) {
$this->runFromCommandLine();
} else {
$this->runFromServerOrCron();
}
});
}
/**
* Runs main program from server or cron job
*/
private function runFromServerOrCron()
{
self::writeOutput('Running the KommaTerug backup program v.'.self::version, self::COLOR_GREEN);
if (!$this->parseConfigFileFromLaravel($this->projectConfigFileName)) {
self::writeOutput('Could not parse laravel\'s config file');
return false;
}
self::writeOutput('Lets create a back-up.', self::COLOR_GREEN);
$backupFolder = $this->createBackupFolderIfNotExists();
if (!$backupFolder) return false;
if(!$this->backupDatabase($backupFolder)) return false;
self::writeOutput('Backup was created successfully!', self::COLOR_GREEN);
return true;
}
/**
* Sends headers to prevent caching.
*/
private static function sendHeadersToPreventCaching()
{
header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
}
/**
* Runs main program from command line. Allowing user interaction
*/
private function runFromCommandLine()
{
self::writeOutput('Thanks for using the KommaTerug backup program.', self::COLOR_GREEN);
$this->setConfigFileNameFromCommandLine();
if (!$this->parseConfigFileFromLaravel($this->projectConfigFileName)) {
return false;
}
if (!$this->letUserChooseAnAction()) {
return false;
}
return true;
}
/**
* Lets the user choose what to do. Like backing up, restoring etc.
*
* @return boolean
*/
private function letUserChooseAnAction()
{
self::writeOutput('What is it what you want to do now? (nothing)', self::COLOR_GREEN);
self::writeOutput('Choose between: "Backup", "Restore"', self::COLOR_GREEN);
switch (strtolower(self::readInput())) {
case 'backup':
self::writeOutput('Smart choice! Lets create a back-up.', self::COLOR_GREEN);
$backupFolder = $this->createBackupFolderIfNotExists();
if (!$backupFolder) return false;
if(!$this->backupDatabase($backupFolder)) return false;
self::writeOutput('Backup was created successfully! Now, go do something awesome.', self::COLOR_GREEN);
return true;
case 'restore';
self::writeOutput('Have faith my young Padawan. I am going to help you to restore the database.', self::COLOR_GREEN);
$this->chooseAndRunDatabaseRestoreStrategy();
return true;
default:
self::writeOutput('You choose to do nothing. Have a nice day.', self::COLOR_GREEN);
return true;
}
}
/**
* Creates an SQL file representing a backup from the database
*/
private function backupDatabase($backupFolder)
{
return $this->chooseAndRunDatabaseBackupStrategy($backupFolder);
}
/**
* Creates a backup folder and returns its path.
* It wil be created in the folder specified in the folder variable
*/
private function createBackupFolderIfNotExists()
{
$name = "backup_".date('d-m-Y_H.i.s');
$folder = self::$folder.$name;
if(!self::createFolderIfNotExists($folder)) return false;
self::writeOutput('Created backup folder: '.$folder, self::COLOR_GREEN);
return $folder;
}
/**
* Count the amount of backup folders / files
*
* @return int
*/
private static function countBackups()
{
$found = scandir(self::$folder);
$count = 0;
foreach($found as $value)
{
if(strpos($value, 'backup') !== false) $count++;
}
return $count;
}
/**
* Let the system choose one method to backup the database
*/
private function chooseAndRunDatabaseBackupStrategy($backupFolder)
{
$mysqldumpExecutable = self::getConfig('mysqldump_executable', 'mysqldump');
if(self::systemCommandExists($mysqldumpExecutable)) {
self::writeOutput("Using the mysqldump system command to backup your database...", self::COLOR_GREEN);
return $this->backupDatabaseUsingMySQLDump($backupFolder);
} else {
// if (!$this->initializeMySQLDatabaseConnection()) return false; //TODO implement way to backup via php
}
self::writeOutput('Could not backup your database since no suitable backup strategy could be found');
$this->showMysqlToolLocations();
return false;
}
/**
* Let the system choose a method to restore the database
*
* @return bool
*/
private function chooseAndRunDatabaseRestoreStrategy()
{
$mysqlImportExecutable = self::getConfig('mysqlimport_executable', 'mysqlimport');
$mysqlExecutable = self::getConfig('mysql_executable', 'mysql');
if(self::systemCommandExists($mysqlExecutable)) {
self::writeOutput("Using the mysql system command to restore your database...", self::COLOR_GREEN);
return $this->restoreDatabaseUsingMySQL();
}
else if(self::systemCommandExists($mysqlImportExecutable)) {
self::writeOutput("Using the mysqlimport system command to restore your database...", self::COLOR_GREEN);
return $this->restoreDatabaseUsingMySQLImport();
} else {
// if (!$this->initializeMySQLDatabaseConnection()) return false; //TODO implement way to restore via php
}
self::writeOutput('Could not restore your database since no suitable restore strategy could be found');
return false;
}
/**
* Determines if a command exists on the current environment
*
* @param string $command The command to check
* @return bool true if the command has been found. false otherwise
*/
private static function systemCommandExists ($command) {
$whereIsCommand = (PHP_OS == 'WINNT') ? 'where' : 'which';
$process = proc_open(
$whereIsCommand.' '.$command,
[
["pipe", "r"], //STDIN
["pipe", "w"], //STDOUT
["pipe", "w"] //STDERR
],
$pipes
);
if ($process !== false) {
$stdout = stream_get_contents($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
return $stdout != '';
}
return false;
}
/**
* executes a system command
*
* @param string $command The command to execute
* @param bool $showCommandBeforeExecuting
* @return bool true if the command was executed successfully. false otherwise
*/
private static function executeSystemCommand ($command, $showCommandBeforeExecuting = false) {
if($showCommandBeforeExecuting) self::writeOutput('Running system command: '.$command);
$process = proc_open(
$command,
[
["pipe", "r"], //STDIN
["pipe", "w"], //STDOUT
["pipe", "w"] //STDERR
],
$pipes
);
if ($process !== false) {
$stdout = stream_get_contents($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
if ($stderr && strpos($stderr, 'Warning') === false) {
self::writeOutput($stderr, self::COLOR_LIGHT_GRAY);
return false;
}
if($stdout) self::writeOutput($stdout, self::COLOR_LIGHT_GRAY);
return true;
}
return false;
}
/**
* Opens a MySQL database connection.
*
* @return bool
*/
private function initializeMySQLDatabaseConnection()
{
if(
!$this->dbHostWrite || !$this->dbDatabaseWrite || !$this->dbUsernameWrite || !$this->dbPasswordWrite ||
!$this->dbHostRead || !$this->dbDatabaseRead || !$this->dbUsernameRead || !$this->dbPasswordRead
) {
self::writeOutput('Error. please make sure you set your database host, database, username and password in your projects config (not the script config)');
return false;
}
$dsnWriteParts = [
'host='.$this->dbHostWrite,
'dbname='.$this->dbDatabaseWrite,
];
if($this->dbPortWrite) $dsnWriteParts[] = 'port='.intval($this->dbPortWrite);
$dsnWrite = 'mysql:'.implode(';', $dsnWriteParts);
$dsnReadParts = [
'host='.$this->dbHostRead,
'dbname='.$this->dbDatabaseRead,
];
if($this->dbPortRead) $dsnReadParts[] = 'port='.intval($this->dbPortRead);
$dsnRead = 'mysql:'.implode(';', $dsnReadParts);
try {
$this->PDOWrite = new PDO($dsnWrite, $this->dbUsernameWrite, $this->dbPasswordWrite);
}
catch (Exception $exception)
{
self::writeOutput('Could not connect to MySQL database for writing with the current settings.');
self::writeOutput($exception->getMessage());
return false;
}
try {
$this->PDORead = new PDO($dsnRead, $this->dbUsernameRead, $this->dbPasswordRead);
}
catch (Exception $exception)
{
self::writeOutput('Could not connect to MySQL database for reading with the current settings.');
self::writeOutput($exception->getMessage());
return false;
}
return true;
}
/**
* Backup database using MySQL dump
*
* @return bool
*/
private function backupDatabaseUsingMySQLDump($folder)
{
$mysqldumpExecutable = $this->getConfig('mysqldump_executable', 'mysqldump');
$commandDisplay = $mysqldumpExecutable.' --databases '.$this->dbDatabaseRead.' > '.$folder.DIRECTORY_SEPARATOR.'database.sql';
$command = $mysqldumpExecutable.' -u '.$this->dbUsernameRead.' -p'.$this->dbPasswordRead.' --databases '.$this->dbDatabaseRead.' > '.$folder.DIRECTORY_SEPARATOR.'database.sql';
self::writeOutput('executing mysql command like this (credentials excluded): "'.$commandDisplay.'"', self::COLOR_GREEN);
self::writeOutput('Backing up database "'.$this->dbDatabaseRead.'". Please standby...', self::COLOR_GREEN);
if(!self::executeSystemCommand($command)) {
self::writeOutput('Well...that did not work. I don\'t know what to do next, so I\'ll stop.', self::COLOR_GREEN);
return false;
}
self::writeOutput('Database "'.$this->dbDatabaseRead.'" backupped! It\'s here: '.$folder.DIRECTORY_SEPARATOR.'database.sql', self::COLOR_GREEN);
return true;
}
/**
* Let the user select a backup
*
* @return string The name of the backup relative to the folder
*/
private function selectBackup()
{
$backupNames = $this->listBackups();
if(count($backupNames) == 0) {
self::writeOutput("There are no backups. So i will stop.", self::COLOR_GREEN);
return false;
}
self::writeOutput('Type the number of a backup to select it: ', self::COLOR_GREEN);
$backupNumber = self::readInput();
if($backupNumber == "") $this->selectBackup();
if(!in_array($backupNumber, array_keys($backupNames))) {
self::writeOutput('There is no backup number '.$backupNumber.'.', self::COLOR_GREEN);
return $this->selectBackup();
}
return $backupNames[$backupNumber];
}
/**
* Asks the user a couple of questions to restore a database and then restores it using the mysqlimport programm
*
* @return bool true if restored successfully. false if not.
*/
private function restoreDatabaseUsingMySQLImport()
{
$backupName = $this->selectBackup();
if(!$backupName) return false;
$mysqlImportExecutable = $this->getConfig('mysqlimport_executable', 'mysqlimport');
$systemCommand = $mysqlImportExecutable.' -u '.$this->dbUsernameWrite.' -p'.$this->dbPasswordWrite.' '.$this->dbDatabaseWrite.' '.self::$folder.$backupName.DIRECTORY_SEPARATOR.'database.sql';
$systemCommandDisplay = $mysqlImportExecutable.' '.$this->dbDatabaseWrite.' '.self::$folder.$backupName.DIRECTORY_SEPARATOR.'database.sql';
self::writeOutput('Restoring database with name: '.$backupName.' into configured database "'.$this->dbDatabaseWrite.'", using command (credentials excluded): ', self::COLOR_GREEN);
self::writeOutput($systemCommand, self::COLOR_GREEN);
if(!self::executeSystemCommand($systemCommand)) {
self::writeOutput('Well...that did not work. I don\'t know what to do next, so I\'ll stop.', self::COLOR_GREEN);
return false;
}
self::writeOutput('Database restored successfully', self::COLOR_GREEN);
return true;
}
/**
* Asks the user a couple of questions to restore a database and then restores it using the mysql programm
*
* @return bool true if restored successfully. false if not.
*/
private function restoreDatabaseUsingMySQL()
{
$backupName = self::selectBackup();
if(!$backupName) return false;
$mysqlExecutable = $this->getConfig('mysql_executable', 'mysql');
$systemCommand = $mysqlExecutable.' -u '.$this->dbUsernameWrite.' -p'.$this->dbPasswordWrite.' '.$this->dbDatabaseWrite.' < '.self::$folder.$backupName.DIRECTORY_SEPARATOR.'database.sql';
$systemCommandDisplay = $mysqlExecutable.' '.$this->dbDatabaseWrite.' < '.self::$folder.$backupName.DIRECTORY_SEPARATOR.'database.sql';
self::writeOutput('Restoring database backup '.$backupName.' into configured database "'.$this->dbDatabaseWrite.'", using command (credentials excluded): ', self::COLOR_GREEN);
self::writeOutput($systemCommandDisplay, self::COLOR_GREEN);
if(!self::executeSystemCommand($systemCommand)) {
self::writeOutput('Well...that did not work. I don\'t know what to do next, so I\'ll stop.', self::COLOR_GREEN);
return false;
}
self::writeOutput('Database restored successfully!', self::COLOR_GREEN);
return true;
}
/**
* Returns backup folder / file names and shows them in a nicely formatted way so that a user can choose one.
*
* @param bool $showToUser
* @return array
*/
private function listBackups($showToUser = true)
{
$found = scandir(self::$folder, 0);
$count = 1;
$backupNames = [];
foreach($found as $value)
{
if(strpos($value, 'backup') !== false) {
$dirSizeBytes = self::directorySize(self::$folder.DIRECTORY_SEPARATOR.$value);
$dirSizeInMegaBytes = round($dirSizeBytes / 1024 / 1024, 2);
$backupNames[$count] = $value;
if($showToUser) self::writeOutput($count.') '.$backupNames[$count].' ('.$dirSizeInMegaBytes.' MB)', self::COLOR_LIGHT_GRAY);
$count++;
}
}
return $backupNames;
}
/**
* Returns log file names and shows them in a nicely formatted way so that a user can choose one.
*
* @param bool $showToUser
* @return array
*/
private function listLogFiles($showToUser = true)
{
$found = scandir(self::$folder.self::$logFolderName, 0);
$count = 1;
$logNames = [];
foreach($found as $value)
{
if($value == '.' || $value == '..') continue;
$dirSizeBytes = self::directorySize(self::$folder.self::$logFolderName.DIRECTORY_SEPARATOR.$value);
$dirSizeInMegaBytes = round($dirSizeBytes / 1024 / 1024, 2);
$logNames[$count] = $value;
if($showToUser) self::writeOutput($count.') '.$logNames[$count].' ('.$dirSizeInMegaBytes.' MB)', self::COLOR_LIGHT_GRAY);
$count++;
}
return $logNames;
}
/**
* Initializes files and folders this script needs.
*
* Returns true if successful. false if not
*/
private static function initializeFilesAndFolders()
{
if(!self::createFolderIfNotExists(self::$folder)) return false;
if(!self::createScriptConfigFileIfNotExists(self::$folder.self::$scriptConfigFileName)) return false;
if(!self::createFolderIfNotExists(self::$folder. self::$logFolderName)) return false;
return true;
}
/**
* Creates a folder if it does not exist.
* Returns true if the folder was created or when it exists. false otherwise
*
* @param $folder
* @param $mode
* @return bool
*/
private static function createFolderIfNotExists($folder, $mode = 0777)
{
if(!file_exists($folder))
{
if(!@mkdir($folder, $mode, false)) {
self::writeOutput('Could not create folder: '.$folder.'');
return false;
}
}
return true;
}
/**
* Create a config file for the script. Resides inside the folder
* @param $configFile
* @return bool
*/
private static function createScriptConfigFileIfNotExists($configFile)
{
if(!file_exists($configFile))
{
if(!@touch($configFile, false)) {
self::writeOutput('Could not create config file: '.$configFile.'');
return false;
}
file_put_contents($configFile, [
'<?php'.PHP_EOL,
'return ['.PHP_EOL,
' "max_backup_size" => 1000, //Megabytes'.PHP_EOL,
' "max_log_size" => 10, //Megabytes for all log files together'.PHP_EOL,
' "mysqldump_executable" => "/usr/local/bin/mysqldump", //Path to mysqldump executable. The one for Mamp is /Applications/MAMP/Library/bin/mysqldump'.PHP_EOL,
' "mysqlimport_executable" => "/usr/local/bin/mysqlimport", //Path to mysqldump executable. The one for Mamp is /Applications/MAMP/Library/bin/mysqlimport'.PHP_EOL,
' "mysql_executable" => "/usr/local/bin/mysql", //Path to mysql executable. The one for Mamp is /Applications/MAMP/Library/bin/mysql'.PHP_EOL,
'];'.PHP_EOL
]);
}
return true;
}
/**
* Sets the project config file name by prompting the user for its name or using a default one.
*/
private function setConfigFileNameFromCommandLine()
{
self::writeOutput('What is the name of your projects config file you want to use: ('.$this->projectConfigFileName.'): ', self::COLOR_GREEN);
self::writeOutput('Notice that this is not the config file from the KommaTerug script.', self::COLOR_GREEN);
$input = self::readInput();
if($input != "") $this->projectConfigFileName = $input;
if(file_exists($this->projectConfigFileName)) {
self::writeOutput('Ok. We will use the file "'.$this->projectConfigFileName.'" as the config file', self::COLOR_GREEN);
} else {
self::writeOutput('The file "'.$this->projectConfigFileName.'" does not exist. Please try again', self::COLOR_RED);
$this->setConfigFileNameFromCommandLine();
}
}
/**
* Reads configuration data from the configuration file
*/
private function parseScriptConfigFile()
{
$path = self::$folder.self::$scriptConfigFileName;
$this->config = require($path);
return true;
}
/**
* Gets a config value OR the specified default value
*
* @param $key
* @param null|mixed $default A default value to return if the key could not be found in the configuration
* @return mixed|null
*/
private function getConfig($key, $default = null)
{
$keys = explode('.', $key);
if($key == $keys)
{
//no array. Just return the value or default
return (isset($this->config[$key])) ? $this->config[$key] : $default;
} else {
$currentConfig = $this->config;
while($keys)
{
$currentKey = array_shift($keys);
if(!isset($currentConfig[$currentKey])) return $default;
if(count($keys) == 0) return isset($currentConfig[$currentKey]) ? $currentConfig[$currentKey] : $default;
$currentConfig = $currentConfig[$currentKey];
}
}
return $default;
}
/**
* Reads a laravel environment file to array
*
* @param $configFileName
* @return bool
*/
private function parseConfigFileFromLaravel($configFileName)
{
$configData = file_get_contents(__DIR__.DIRECTORY_SEPARATOR.$configFileName);
$configDataLines = explode(PHP_EOL, $configData);
foreach($configDataLines as $line)
{
if(trim($line) == "") continue;
$parts = explode('=', $line);
$option = $parts[0];
$value = $parts[1];
switch (strtolower($option))
{
case "db_host";
$parts = explode(':', $value);
if($parts !== $value) {
$this->dbHostRead = $this->dbHostWrite = $parts[0];
$this->dbPortRead = $this->dbPortWrite = $parts[1];
} else {
$this->dbHostRead = $this->dbHostWrite = $value;
}
break;
case "db_database";
$this->dbDatabaseRead = $this->dbDatabaseWrite = $value;
break;
case "db_port";
$this->dbPortRead = $this->dbPortWrite = $value;
break;
case "db_username";
$this->dbUsernameRead = $this->dbUsernameWrite = $value;
break;
case "db_password";
$this->dbPasswordRead = $this->dbPasswordWrite = $value;
break;
}
}
return true;
}
/**
* Read input from the command line
*
* @param string $default
* @return string
*/
private static function readInput($default = '')
{
$line = trim(fgets(STDIN));
return ($line != "") ? $line : $default;
}
/**
* Colors a string using one of the COLOR_ constants on the top of this class
*
* @param $string
* @param $color
* @return string
*/
private static function color($string, $color)
{
return "\033[".$color."m".$string."\033[0m";
}
/**
* @param string $string
* @param bool $isError
* @param null|int $color One of the COLOR_ constants on the top of this class
*/
private static function writeOutput($string, $color = null, $isError = false)
{
if(self::runsFromCommandLine()) {
$string = $string.PHP_EOL;
if($color) $string = self::color($string, $color);
fwrite(STDOUT, $string);
if ($isError) {
fwrite(STDERR, $string);
}
}
else
{
echo $string.'<br>';
}
self::log($string);
}
/**
* Log string to a file of today
*
* @param $string
* @return bool true on success, false on failure
*/
private static function log($string)
{
$string = self::getTimeLog().$string.PHP_EOL;
$logFile = self::$folder.self::$logFolderName.self::getTodaysLogFileName();
if(file_put_contents($logFile, $string, FILE_APPEND) === false) return false;
return true;
}
/**
* Get today's log file name. Relative to the logs folder
*/
private static function getTodaysLogFileName()
{
$name = 'log_';
$name .= date('d-m-Y');
$name .= '.txt';
return $name;
}
/**
* Returns true if php is ran from the command line. false if not.
*
* @return bool
*/
private static function runsFromCommandLine()
{
if(php_sapi_name() == 'cli') {
return true;
}
else {
return false;
}
}
/**
* Dump data
*
* @param $data
* @param bool $verbose
*/
private static function dd($data, $verbose = false)
{
if($verbose) {
var_dump($data);
} else {
print_r($data);
}
die(PHP_EOL);
}
/**
* Convert an arguments array to a string
* @param $arguments
* @return string
*/
private static function argumentsArrayToString(...$arguments)
{
if(!is_array($arguments) && count($arguments) == 0) return '';
return implode(',', $arguments);
}
/**
* Returns a time log string. Example. A string like this: "[24-07-2018 10:15:01]"
*/
private static function getTimeLog()
{
$string = date('d/m/Y H:i:s');
$string = '['.$string.'] ';
return $string;
}
/**
* Do some cleaning up. Like removing old log files and backups
*/
private function cleanup()
{
self::writeOutput('Cleaning up old stuff', self::COLOR_GREEN);
$this->deleteOldBackups();
$this->deleteOldLogs();
self::writeOutput('Done cleaning up!', self::COLOR_GREEN);
}
/**
* Deletes old backups if the size of the backups exceeds the config's max_backup_size variable
*/
private function deleteOldBackups()
{
$backupsToDelete = $this->findOldBackups();
foreach($backupsToDelete as $backupName)
{
self::writeOutput('Deleting old backup: '.$backupName);
$backupPath = self::$folder.$backupName;
if(is_dir($backupPath)) {
self::deleteDir($backupPath);
}
}
}
/**
* Deletes old logs if the size of the logs exceeds the config's max_log_size variable
*/
private function deleteOldLogs()
{
$logsToDelete = $this->findOldLogs();
foreach($logsToDelete as $logToDelete)
{
self::writeOutput('Deleting old log: '.$logToDelete);
$logPath = self::$folder.self::$logFolderName.$logToDelete;
if(!unlink($logPath)) {
self::writeOutput('Could not delete log: '.$logPath);
}
}
}
/**
* Finds old backups when the size of the backups in total exceed the config's max_backup_size variable
*/
private function findOldBackups()
{
$maxBackupSizeInBytes = self::getConfig('max_backup_size', 500) * 1024 * 1024;
$backupNames = array_reverse($this->listBackups(false)); //First one is newest
$currentSize = 0; //In bytes
$backupsPathsToKeep = [];
$backupPathsToDelete = [];
foreach($backupNames as $index => $backupName)
{
$backupPath = self::$folder.$backupName;
$currentSize += self::directorySize($backupPath);
if($currentSize < $maxBackupSizeInBytes) {
$backupsPathsToKeep[] = $backupName;
} else {
$backupPathsToDelete[] = $backupName;
}
}
return $backupPathsToDelete;
}
/**
* Finds old backups when the size of the backups in total exceed the config's max_backup_size variable
*/
private function findOldLogs()
{
$maxLogSizeInBytes = self::getConfig('max_log_size', 50) * 1024 * 1024;
$logNames = array_reverse($this->listLogFiles(false)); //First one is newest
$currentSize = 0; //In bytes
$logPathsToKeep = [];
$logPathsToDelete = [];
foreach($logNames as $index => $logName)
{
$logPath = self::$folder.self::$logFolderName.$logName;
$currentSize += filesize($logPath);
if($currentSize < $maxLogSizeInBytes) {
$logPathsToKeep[] = $logName;
} else {
$logPathsToDelete[] = $logName;
}
}
return $logPathsToDelete;
}
/**
* Executes the callback and handles its exceptions
*
* @param $callback
*/
private static function handleExceptionsIfAny($callback)
{
try {
$callback();
}
catch (Exception $exception)
{
self::writeOutput(self::getTimeLog().'Exception on line: '.$exception->getLine().' "'.$exception->getMessage().'"');
self::writeOutput('Tracelog: ');
$trace = $exception->getTrace();
foreach($trace as $traceLine)
{
self::writeOutput($traceLine['file'].':'.$traceLine['line'].'@'.$traceLine['function'].' arguments: '.self::argumentsArrayToString($traceLine['args']));
}
}
}
/**
* Deletes a directory with it's contents if any.
*
* @param $dirPath
*/
public static function deleteDir($dirPath) {
if (! is_dir($dirPath)) {
throw new InvalidArgumentException("$dirPath must be a directory");
}
if (substr($dirPath, strlen($dirPath) - 1, 1) != '/') {
$dirPath .= '/';
}
$files = glob($dirPath . '*', GLOB_MARK);
foreach ($files as $file) {
if (is_dir($file)) {
self::deleteDir($file);
} else {
unlink($file);
}
}
rmdir($dirPath);
}
/**
* Show MySQL tool locations
*/
public function showMysqlToolLocations()
{
$whereIsCommand = (PHP_OS == 'WINNT') ? 'where' : 'which';
self::writeOutput('Trying to show you the mysql tool locations:');
self::executeSystemCommand($whereIsCommand." mysql", true);
self::executeSystemCommand($whereIsCommand." mysqlimport", true);
self::executeSystemCommand($whereIsCommand." mysqldump", true);
self::executeSystemCommand($whereIsCommand." mysql.exe", true);
self::executeSystemCommand($whereIsCommand." mysqlimport.exe", true);
self::executeSystemCommand($whereIsCommand." mysqldump.exe", true);
}
/**
* Returns the folder size in bytes
*
* @param $dir
* @return int
*/
private static function directorySize($dir)
{
$size = 0;
foreach (glob(rtrim($dir, '/').'/*', GLOB_NOSORT) as $each) {
$size += is_file($each) ? filesize($each) : self::directorySize($each);
}
return $size;
}
}
(new KommaTerug())->run();