From cb56c557825559fda10aec821970af80bb86e85b Mon Sep 17 00:00:00 2001 From: Mougrim Date: Sun, 9 Jul 2023 10:55:14 +0300 Subject: [PATCH] (#1) Up php to 8.1 Changes: - Up php to 8.1 - Change mougrim/php-logger to own psr/log compatible logger - Add type declaring and hints everywhere - Add declare(strict_types=1) to all php-files - Pass parameters to constructor instead of using setters - Use psr-4 autoload instead of psr-0 - Use readonly properties instead of getters - Add badges to README.md - Chane placeholders in param values using param values --- CHANGELOG.md | 19 ++ README.md | 5 + app/{Mougrim/Deployer => }/Command/Deploy.php | 152 ++++----- app/Command/Help.php | 123 +++++++ .../Deployer => }/Helper/ShellHelper.php | 68 ++-- app/Helper/TemplateHelper.php | 101 ++++++ app/Kernel/AbstractCommand.php | 212 +++++++++++++ app/Kernel/Application.php | 123 +++++++ app/{Mougrim/Deployer => }/Kernel/Request.php | 35 +- app/Logger/Logger.php | 101 ++++++ app/Mougrim/Deployer/Command/Help.php | 112 ------- .../Deployer/Helper/TemplateHelper.php | 100 ------ .../Deployer/Kernel/AbstractCommand.php | 299 ------------------ app/Mougrim/Deployer/Kernel/Application.php | 137 -------- bin/mougrim-deployer.php | 27 +- composer.json | 9 +- composer.lock | 57 ++-- config/logger.php | 53 +--- 18 files changed, 875 insertions(+), 858 deletions(-) create mode 100644 CHANGELOG.md rename app/{Mougrim/Deployer => }/Command/Deploy.php (69%) create mode 100644 app/Command/Help.php rename app/{Mougrim/Deployer => }/Helper/ShellHelper.php (56%) create mode 100644 app/Helper/TemplateHelper.php create mode 100644 app/Kernel/AbstractCommand.php create mode 100644 app/Kernel/Application.php rename app/{Mougrim/Deployer => }/Kernel/Request.php (59%) create mode 100644 app/Logger/Logger.php delete mode 100644 app/Mougrim/Deployer/Command/Help.php delete mode 100644 app/Mougrim/Deployer/Helper/TemplateHelper.php delete mode 100644 app/Mougrim/Deployer/Kernel/AbstractCommand.php delete mode 100644 app/Mougrim/Deployer/Kernel/Application.php diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..9829647 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,19 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Changed +- Up php to 8.1 +- Change mougrim/php-logger to own psr/log compatible logger +- Add type declaring and hints everywhere +- Add declare(strict_types=1) to all php-files +- Pass parameters to constructor instead of using setters +- Use psr-4 autoload instead of psr-0 +- Use readonly properties instead of getters +- Add badges to README.md +- Chane placeholders in param values using param values diff --git a/README.md b/README.md index b617be7..39f5039 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,11 @@ # php-mougrim-deployer + `php-mougrim-deployer` is tool for seamless deploy you git project. +[![Latest Stable Version](https://poser.pugx.org/mougrim/deployer/version)](https://packagist.org/packages/mougrim/deployer) +[![Latest Unstable Version](https://poser.pugx.org/mougrim/deployer/v/unstable)](https://packagist.org/packages/mougrim/deployer) +[![License](https://poser.pugx.org/mougrim/deployer/license)](https://packagist.org/packages/mougrim/deployer) + ## Benefits `php-mougrim-deployer` gives the following benefits: diff --git a/app/Mougrim/Deployer/Command/Deploy.php b/app/Command/Deploy.php similarity index 69% rename from app/Mougrim/Deployer/Command/Deploy.php rename to app/Command/Deploy.php index f3d2a9c..cd93792 100644 --- a/app/Mougrim/Deployer/Command/Deploy.php +++ b/app/Command/Deploy.php @@ -1,16 +1,26 @@ + * @author Mougrim */ class Deploy extends AbstractCommand { - static public function getActionsSubActions() + static public function getActionsSubActions(): array { return [ 'index' => [ @@ -26,7 +36,7 @@ static public function getActionsSubActions() ]; } - static public function getRawRequestParamsInfo() + static public function getRawRequestParamsInfo(): array { return [ 'init' => [ @@ -76,7 +86,7 @@ static public function getRawRequestParamsInfo() 'multiple' => true, 'info' => 'Scripts, run after deploy to new tag, as template', ], - 'template-files' => [ + 'template-files' => [ 'multiple' => true, 'info' => implode( "\n", @@ -96,7 +106,7 @@ static public function getRawRequestParamsInfo() ] ), ], - 'template-parameters' => [ + 'template-parameters' => [ 'multiple' => true, 'info' => "Custom template parameters, passed to templates - files in 'template-files' and commands names", ], @@ -137,18 +147,18 @@ static public function getRawRequestParamsInfo() ]; } - static public function getInfo() + static public function getInfo(): string { return 'deploy your project'; } - protected function getAdditionalParams() + protected function getAdditionalParams(): array { - $applicationPath = $this->getRequestParam('application-path'); - $actionAdditionalParams = []; + $applicationPath = (string) $this->getRequestParam('application-path'); + $actionAdditionalParams = []; $actionAdditionalParams['versions-path'] = "{$applicationPath}/versions"; if ($this->requestParamExists('tag')) { - $tag = $this->getRequestParam('tag'); + $tag = (string) $this->getRequestParam('tag'); $actionAdditionalParams['version-path'] = "{$actionAdditionalParams['versions-path']}/{$tag}"; } $actionAdditionalParams['current-link-path'] = "{$actionAdditionalParams['versions-path']}/current"; @@ -160,78 +170,80 @@ protected function getAdditionalParams() return $additionalParams; } - private $templateHelper; + private TemplateHelper $templateHelper; - public function getTemplateHelper() + private function getTemplateHelper(): TemplateHelper { - if ($this->templateHelper === null) { - $this->templateHelper = new TemplateHelper(); - $this->templateHelper->setShellHelper($this->getShellHelper()); + if (!isset($this->templateHelper)) { + $this->templateHelper = new TemplateHelper( + shellHelper: $this->shellHelper, + logger: $this->logger, + ); } return $this->templateHelper; } - public function runTemplateCommand($command) + public function runTemplateCommand($command): void { $command = $this->getTemplateHelper()->processTemplateString($command, $this->getParams()); - $this->getShellHelper()->runCommand($command); + $this->shellHelper->runCommand($command); } /** - * 1. Инициализация (создание дирректорий, симлинок) + * 1. Initialization (create dirs, symlinks) * 2. git fetch origin -p * 3. git checkout tag * 4. sync * 5. Change symlink * 6. nginx reload */ - public function actionIndex() + public function actionIndex(): void { $this->actionInit(); $this->actionDeploy(); $this->actionSwitch(); } - public function actionInit() + public function actionInit(): void { $applicationPath = $this->getParam('application-path'); $user = $this->getParam('user'); $group = $this->getParam('group'); $versionsPath = $this->getParam('versions-path'); - $this->getLogger()->info("Init..."); + $this->logger->info('Init...'); if (!file_exists($applicationPath)) { - $this->getShellHelper()->sudo()->mkdir($applicationPath, true); - $this->getShellHelper()->sudo()->mkdir($versionsPath); - $this->getShellHelper()->sudo()->chown($user, $group, $applicationPath, true); - $this->getLogger()->info("Init completed"); + $this->shellHelper->sudo()->mkdir($applicationPath, true); + $this->shellHelper->sudo()->mkdir($versionsPath); + $this->shellHelper->sudo()->chown($user, $group, $applicationPath, true); + $this->logger->info('Init completed'); } else { - $this->getLogger()->info("Already inited"); + $this->logger->info('Already init'); } } - public function actionDeploy() + public function actionDeploy(bool $isSkipGit = false): void { - $user = $this->getParam('user'); - $tag = $this->getParam('tag'); + $user = (string) $this->getParam('user'); + $tag = (string) $this->getParam('tag'); $beforeDeployScripts = $this->getParam('before-deploy-script'); $afterDeployScripts = $this->getParam('after-deploy-script'); - $isSkipGit = (boolean) $this->getParam('skip-git'); + $isSkipGit = $this->getParam('skip-git') || $isSkipGit; $isSkipDeployFiles = (boolean) $this->getParam('skip-deploy-files'); $currentLink = $this->getParam('current-link-path'); - $versionPath = $this->getParam('version-path'); + $versionPath = (string) $this->getParam('version-path'); $templateFiles = $this->getParam('template-files'); - $this->getLogger()->info("Before deploy"); + $this->logger->info("Before deploy"); if (!$isSkipGit) { - $this->getShellHelper()->runCommand("git status"); - $this->getShellHelper()->runCommand("git fetch origin -p"); + $this->shellHelper->runCommand("git status"); + $this->shellHelper->runCommand("git fetch origin -p"); // checkout tag for run actual scripts - $this->getShellHelper()->runCommand("git checkout " . escapeshellarg($tag)); + $this->shellHelper->runCommand("git checkout " . escapeshellarg($tag)); } if ($beforeDeployScripts !== null) { - $this->getLogger()->info("Run before deploy scripts"); + $this->logger->info("Run before deploy scripts"); foreach ($beforeDeployScripts as $beforeDeployScript) { $this->runTemplateCommand($beforeDeployScript); } @@ -240,58 +252,57 @@ public function actionDeploy() if (!$isSkipDeployFiles) { if (file_exists($versionPath)) { if (realpath($currentLink) === $versionPath) { - $this->getLogger()->warn( + $this->logger->warning( "Current link is point to {$versionPath}, are you sure to remove it? (yes/no) [no]" ); } else { - $this->getLogger()->info("Directory '{$versionPath}' already exists, remove it? (yes/no) [no]"); + $this->logger->info("Directory '{$versionPath}' already exists, remove it? (yes/no) [no]"); } $input = trim(fgets(STDIN)); if ($input !== "yes") { - $this->getLogger()->info("Cancel"); - $this->getApplication()->end(0); - return; + $this->logger->info("Cancel"); + $this->application->end(); } - $this->getLogger()->info("Remove exists directory {$versionPath}"); - $this->getShellHelper()->sudo()->rm($versionPath, true); + $this->logger->info("Remove exists directory {$versionPath}"); + $this->shellHelper->sudo()->rm($versionPath, true); } - $this->getShellHelper()->sudo($user)->mkdir($versionPath); + $this->shellHelper->sudo($user)->mkdir($versionPath); - $this->getShellHelper()->runCommand( - 'tar -c ./ --exclude=.git |\\ + $this->shellHelper->runCommand( + 'tar -c --exclude=.git ./ |\\ sudo -u ' . escapeshellarg($user) . ' tar -x -C ' . escapeshellarg($versionPath) ); } if ($templateFiles !== null) { - $this->getLogger()->info("Process template files"); + $this->logger->info("Process template files"); foreach ($templateFiles as $templateFile) { - $this->getLogger()->info("Process template file {$templateFile}"); + $this->logger->info("Process template file {$templateFile}"); $templateFile = $this->getTemplateHelper()->processTemplateString($templateFile, $this->getParams()); - $this->getLogger()->info("Real template file path {$templateFile}"); + $this->logger->info("Real template file path {$templateFile}"); $this->getTemplateHelper()->processTemplateToFile($user, $templateFile, $this->getParams()); } } if ($afterDeployScripts !== null) { - $this->getLogger()->info("Run after deploy scripts"); + $this->logger->info("Run after deploy scripts"); foreach ($afterDeployScripts as $afterDeployScript) { $afterDeployScript = strtr($afterDeployScript, ['{{ version_path }}' => $versionPath]); $this->runTemplateCommand($afterDeployScript); } } - $this->getLogger()->info("Deploy complete"); + $this->logger->info("Deploy complete"); } - public function actionSwitch() + public function actionSwitch(): void { - $applicationPath = $this->getParam('application-path'); - $user = $this->getParam('user'); - $afterSwitchScripts = $this->getParam('after-switch-script'); - $currentLink = $this->getParam('current-link-path'); - $versionPath = $this->getParam('version-path'); + $applicationPath = (string) $this->getParam('application-path'); + $user = (string) $this->getParam('user'); + $afterSwitchScripts = $this->getParam('after-switch-script'); + $currentLink = (string) $this->getParam('current-link-path'); + $versionPath = (string) $this->getParam('version-path'); $applicationFiles = scandir($applicationPath); @@ -306,11 +317,11 @@ public function actionSwitch() } } - $this->getLogger()->info("Create new links"); + $this->logger->info("Create new links"); $linksCreated = false; foreach ($versionFiles as $versionFile) { if (!in_array($versionFile, $applicationFiles)) { - $this->getShellHelper()->sudo($user)->ln( + $this->shellHelper->sudo($user)->ln( "{$applicationPath}/{$versionFile}", "$currentLink/{$versionFile}" ); @@ -319,45 +330,44 @@ public function actionSwitch() } if ($linksCreated === false) { - $this->getLogger()->info("No new links found"); + $this->logger->info("No new links found"); } - $this->getLogger()->info("Switch version"); - $this->getShellHelper()->sudo($user)->ln( + $this->logger->info("Switch version"); + $this->shellHelper->sudo($user)->ln( $currentLink, $versionPath ); - $this->getLogger()->info("Remove old links"); + $this->logger->info("Remove old links"); $linksRemoved = false; foreach ($applicationFiles as $applicationFile) { if (!in_array($applicationFile, $versionFiles)) { $linksRemoved = true; - $this->getShellHelper()->sudo($user)->rm("{$applicationPath}/{$applicationFile}"); + $this->shellHelper->sudo($user)->rm("{$applicationPath}/{$applicationFile}"); } } if ($linksRemoved === false) { - $this->getLogger()->info("No links to remove found"); + $this->logger->info("No links to remove found"); } if ($afterSwitchScripts !== null) { - $this->getLogger()->info("Run after switch scripts"); + $this->logger->info("Run after switch scripts"); foreach ($afterSwitchScripts as $afterSwitchScript) { $afterSwitchScript = strtr($afterSwitchScript, ['{{ version_path }}' => $versionPath]); $this->runTemplateCommand($afterSwitchScript); } } - $this->getLogger()->info("Successful complete"); + $this->logger->info("Successful complete"); } - public function actionDev() + public function actionDev(): void { - $this->addRequestParam('skip-git', true); $this->actionInit(); - $this->actionDeploy(); + $this->actionDeploy(true); $this->actionSwitch(); } } diff --git a/app/Command/Help.php b/app/Command/Help.php new file mode 100644 index 0000000..e6c5b74 --- /dev/null +++ b/app/Command/Help.php @@ -0,0 +1,123 @@ + + */ +class Help extends AbstractCommand +{ + static public function getInfo(): string + { + return 'this command'; + } + + static public function getRawRequestParamsInfo(): array + { + return [ + 'defaultParams' => [ + 0 => [ + 'info' => 'action name', + ], + ], + ]; + } + + public function actionIndex(): void + { + $this->logger->info("Usage: mougrim-deployer.php [] []"); + $this->logger->info("Commands list:"); + foreach (static::getAvailableCommands() as $commandName) { + /** @var AbstractCommand $commandClass */ + $commandClass = '\Mougrim\Deployer\Command\\' . ucfirst($commandName); + $this->logger->info("\t" . $commandName . "\t" . $commandClass::getInfo()); + } + } + + public function __call(string $methodName, array $params = []) + { + if (!str_starts_with($methodName, 'action')) { + throw new RuntimeException('Call to undefined method ' . get_class() . '::' . $methodName . '()'); + } + + $commandName = substr($methodName, 6); + /** @var AbstractCommand|string $commandClass */ + $commandClass = '\Mougrim\Deployer\Command\\' . $commandName; + $commandName = lcfirst($commandName); + if (!class_exists($commandClass)) { + throw new RuntimeException("Command '{$commandName}' not exists"); + } + $this->logger->info("NAME"); + $this->logger->info("\t{$commandName} - " . $commandClass::getInfo()); + $this->logger->info(""); + + $description = $commandClass::getDescription(); + if ($description !== null) { + $this->logger->info("DESCRIPTION"); + foreach (explode("\n", $description) as $descriptionLine) { + $this->logger->info("\t{$descriptionLine}"); + } + $this->logger->info(""); + } + + $this->logger->info("ACTIONS"); + foreach ($commandClass::getActionsIdList() as $actionId) { + // todo action description + $this->logger->info("\t{$actionId}" . ($actionId === 'index' ? ' [default]' : '')); + } + + $commandRequestParamsInfo = $commandClass::getRequestParamsInfo(); + $actionId = $this->getRequestParam(0); + if ($actionId === null && in_array('index', $commandClass::getActionsIdList(), true)) { + $actionId = 'index'; + } + if ($actionId !== null && isset($commandRequestParamsInfo[$actionId])) { + if ($actionId === 'index') { + $this->logger->info("OPTIONS for default action {$actionId}"); + } else { + $this->logger->info("OPTIONS for action {$actionId}"); + } + + foreach ($commandRequestParamsInfo[$actionId] as $paramName => $paramInfo) { + if (!is_numeric($paramName)) { + $paramName = "--{$paramName}"; + } + $this->logger->info("\t{$paramName}"); + foreach (explode("\n", $paramInfo['info']) as $paramInfoLine) { + $this->logger->info("\t\t{$paramInfoLine}"); + } + $this->logger->info(""); + } + } + } + + /** + * @return array + */ + static public function getActionsIdList(): array + { + return array_merge(parent::getActionsIdList(), static::getAvailableCommands()); + } + + /** + * @return array + */ + static private function getAvailableCommands(): array + { + $availableCommands = []; + foreach (glob(__DIR__ . '/../Command/*') as $file) { + $commandName = basename($file, ".php"); + $availableCommands[] = lcfirst($commandName); + } + + return $availableCommands; + } +} diff --git a/app/Mougrim/Deployer/Helper/ShellHelper.php b/app/Helper/ShellHelper.php similarity index 56% rename from app/Mougrim/Deployer/Helper/ShellHelper.php rename to app/Helper/ShellHelper.php index 9fbfb29..5b41953 100644 --- a/app/Mougrim/Deployer/Helper/ShellHelper.php +++ b/app/Helper/ShellHelper.php @@ -1,31 +1,31 @@ + * @author Mougrim */ class ShellHelper { - private $logger; - private $sudo = false; - private $sudoUser; - - /** - * @return Logger - */ - public function getLogger() - { - if ($this->logger === null) { - $this->logger = Logger::getLogger('shell-helper'); - } + private bool $sudo = false; + private ?string $sudoUser = null; - return $this->logger; + public function __construct( + private readonly Logger $logger, + ) { } - public function runCommand($command) + public function runCommand(string $command): void { if ($this->sudo) { $commandPrefix = 'sudo '; @@ -38,16 +38,16 @@ public function runCommand($command) $this->sudo = false; $this->sudoUser = null; } - $this->getLogger()->info("Run '{$command}'"); + $this->logger->info("Run '{$command}'"); $descriptorsSpec = [ 0 => ["pipe", "r"], // stdin is a pipe that the child will read from 1 => ["pipe", "w"], // stdout is a pipe that the child will write to 2 => ["pipe", "w"], // stderr is a pipe that the child will write to ]; - $resource = proc_open($command, $descriptorsSpec, $pipes); + $resource = proc_open($command, $descriptorsSpec, $pipes); if (!is_resource($resource)) { - throw new \RuntimeException("Can't create resource for command '{$command}'"); + throw new RuntimeException("Can't create resource for command '{$command}'"); } fclose($pipes[0]); @@ -58,31 +58,31 @@ public function runCommand($command) $result = proc_close($resource); if (!empty($output)) { - $this->getLogger()->info("Output:\n" . $output); + $this->logger->info("Output:\n" . $output); } else { - $this->getLogger()->info("Empty output"); + $this->logger->info("Empty output"); } if (!empty($error)) { - $this->getLogger()->info("Error output:\n" . $error); + $this->logger->info("Error output:\n" . $error); } if ($result !== 0) { - throw new \RuntimeException("Command '{$command}' executed with error code: {$result}"); + throw new RuntimeException("Command '{$command}' executed with error code: {$result}"); } } - public function sudo($user = 'root') + public function sudo(string $user = 'root'): static { $this->sudo = true; $this->sudoUser = $user; return $this; } - public function mkdir($directory, $recursive = false) + public function mkdir(string $directory, bool $recursive = false): void { $this->runCommand('mkdir ' . ($recursive ? '-p ' : '') . escapeshellarg($directory)); } - public function chown($user, $group, $directory, $recursive = false) + public function chown(string $user, string $group, string $directory, bool $recursive = false): void { $this->runCommand( 'chown ' . ($recursive ? '-R ' : '') . @@ -91,27 +91,27 @@ public function chown($user, $group, $directory, $recursive = false) ); } - public function rm($path, $recursive = false) + public function rm(string $path, bool $recursive = false): void { $this->runCommand('rm -f' . ($recursive ? 'r' : '') . ' ' . escapeshellarg($path)); } - public function ln($link, $destination) + public function ln(string $link, string $destination): void { $this->runCommand('ln -sfT ' . escapeshellarg($destination) . ' ' . escapeshellarg($link)); } - public function checkIsWritable($path) + public function checkIsWritable(string $path): void { - $path = escapeshellarg($path); + $path = escapeshellarg($path); $checkCommand = "if [ ! -w {$path} ]; then echo 'Path not writable'; exit 1; else echo 'Path writable'; fi"; $this->runCommand('bash -c ' . escapeshellarg($checkCommand)); } - public function writeFile($path, $content) + public function writeFile(string $path, string $content): void { - $path = escapeshellarg($path); - $content = escapeshellarg($content); + $path = escapeshellarg($path); + $content = escapeshellarg($content); $writeCommand = "echo {$content} > {$path}"; $this->runCommand('bash -c ' . escapeshellarg($writeCommand)); } diff --git a/app/Helper/TemplateHelper.php b/app/Helper/TemplateHelper.php new file mode 100644 index 0000000..0e49561 --- /dev/null +++ b/app/Helper/TemplateHelper.php @@ -0,0 +1,101 @@ + + */ +class TemplateHelper +{ + public function __construct( + private readonly ShellHelper $shellHelper, + private readonly Logger $logger, + ) { + } + + public function processTemplateToFile( + string $user, + string $templatePath, + array $params, + ?string $destinationPath = null, + ): string { + if (!file_exists($templatePath)) { + throw new RuntimeException("Template '{$templatePath}' not exists"); + } + if (!is_readable($templatePath)) { + throw new RuntimeException("Template '{$templatePath}' not readable"); + } + + $this->logger->info("Process template {$templatePath}"); + + if ($destinationPath === null) { + $destinationPath = $templatePath; + } else { + $this->logger->info("Destination path {$destinationPath}"); + } + + /** @var string $destinationPath */ + $destinationDir = dirname($destinationPath); + if (!file_exists($destinationDir)) { + throw new RuntimeException("Destination dir '{$destinationDir}' not exists"); + } + $this->shellHelper->sudo($user)->checkIsWritable($destinationDir); + if (file_exists($destinationPath)) { + $this->shellHelper->sudo($user)->checkIsWritable($destinationPath); + } + + $template = file_get_contents($templatePath); + $template = $this->processTemplateString($template, $params); + $this->shellHelper->sudo($user)->writeFile($destinationPath, $template); + $this->logger->info("Process complete"); + + return $destinationDir; + } + + public function processTemplateString(string $string, array $params): string + { + $replace = $this->prepareParams($params); + + $result = strtr($string, $replace); + if (preg_match_all('/({{ .+? }})/', $result, $matches)) { + $notProcessedVariables = implode(", ", $matches[1]); + throw new RuntimeException("Not all variables processed: {$notProcessedVariables}"); + } + + return $result; + } + + private function prepareParams(array $params): array + { + $params = $this->makeParamsFlatten($params); + foreach ($params as &$value) { + if (is_string($value)) { + $value = strtr($value, $params); + } + } + unset($value); + return $params; + } + + private function makeParamsFlatten(array $params, string $keyPrefix = '', array $replace = []): array + { + foreach ($params as $key => $value) { + $key = $keyPrefix . $key; + if (is_array($value)) { + $replace = $this->makeParamsFlatten($value, $key . '.', $replace); + } else { + $replace["{{ {$key} }}"] = $value; + } + } + + return $replace; + } +} diff --git a/app/Kernel/AbstractCommand.php b/app/Kernel/AbstractCommand.php new file mode 100644 index 0000000..f24632b --- /dev/null +++ b/app/Kernel/AbstractCommand.php @@ -0,0 +1,212 @@ + + */ +abstract class AbstractCommand +{ + /** + * @param array $requestParams + */ + public function __construct( + protected readonly Application $application, + protected readonly ShellHelper $shellHelper, + protected readonly Logger $logger, + protected array $requestParams, + protected readonly string $id, + protected readonly string $actionId, + ) { + } + + static public function getRawRequestParamsInfo(): array + { + return []; + } + + static public function getRequestParamsInfo(): array + { + $requestParamsInfo = static::getRawRequestParamsInfo(); + foreach (static::getActionsSubActions() as $actionId => $subActions) { + if (!isset($requestParamsInfo[$actionId])) { + $requestParamsInfo[$actionId] = []; + } + foreach ($subActions as $subActionId) { + if (isset($requestParamsInfo[$subActionId])) { + $requestParamsInfo[$actionId] = array_merge( + $requestParamsInfo[$subActionId], + $requestParamsInfo[$actionId] + ); + } + } + } + return $requestParamsInfo; + } + + abstract static public function getInfo(): string; + + static public function getDescription(): ?string + { + return null; + } + + /** + * @return array + */ + static public function getActionsIdList(): array + { + $actions = []; + $methods = get_class_methods(get_called_class()); + foreach ($methods as $method) { + if (preg_match('/^action([A-Z][a-zA-Z0-9]*)$/', $method, $matches)) { + $actions[] = lcfirst($matches[1]); + } + } + + return $actions; + } + + static public function getActionsSubActions(): array + { + return []; + } + + static public function getSubActions(string $actionId): array + { + $actionsSubActions = static::getActionsSubActions(); + return $actionsSubActions[$actionId] ?? []; + } + + public function getRequestParam(string|int $name): string|null|array + { + if (!$this->requestParamExists($name)) { + throw new RuntimeException("Unknown param '{$name}'"); + } + + return $this->requestParams[$name]; + } + + public function requestParamExists($name): bool + { + return isset($this->requestParams[$name]) || array_key_exists($name, $this->requestParams); + } + + protected function getAdditionalParams(): array + { + return []; + } + + private array $params = []; + + /** + * @return array + */ + public function getParams(): array + { + if (!isset($this->params[$this->actionId])) { + $params = $this->requestParams; + $additionalParams = $this->getAdditionalParams(); + $subActions = array_merge(static::getSubActions($this->actionId), [$this->actionId]); + foreach ($subActions as $actionId) { + if (isset($additionalParams[$actionId])) { + $params = array_merge($params, $additionalParams[$actionId]); + } + } + $this->params[$this->actionId] = $params; + } + + return $this->params[$this->actionId]; + } + + public function getParam(string|int $name): string|null|array|bool + { + $params = $this->getParams(); + if (!$this->paramExists($name)) { + throw new RuntimeException("Unknown param '{$name}'"); + } + + return $params[$name]; + } + + public function paramExists(string|int $name): bool + { + $params = $this->getParams(); + return isset($params[$name]) || array_key_exists($name, $params); + } + + public function run(): void + { + $actionMethodName = 'action' . ucfirst($this->actionId); + + if (!in_array($this->actionId, static::getActionsIdList(), true)) { + throw new RuntimeException("Unknown action '{$this->actionId}'"); + } + + $this->requestParams = $this->getActionRequestParams($this->actionId); + + $this->$actionMethodName(); + } + + /** + * @return array + */ + private function getActionRequestParams(string $actionId): array + { + $actionsRequestParamsInfo = static::getRequestParamsInfo(); + $requestParamsInfo = $actionsRequestParamsInfo[$actionId] ?? []; + + if (isset($actionsRequestParamsInfo['defaultParams'])) { + foreach ($actionsRequestParamsInfo['defaultParams'] as $paramName => $paramInfo) { + if (!isset($requestParamsInfo[$paramName])) { + $requestParamsInfo[$paramName] = $paramInfo; + } + } + } + $requestParams = $this->requestParams; + $unknownParams = array_diff(array_keys($requestParams), array_keys($requestParamsInfo)); + if ($unknownParams) { + throw new RuntimeException("Unknown params '" . implode("', '", $unknownParams) . "'"); + } + + $emptyRequireParams = []; + foreach ($requestParamsInfo as $paramName => $paramInfo) { + if (!isset($requestParams[$paramName])) { + if (isset($paramInfo['require']) && $paramInfo['require']) { + $emptyRequireParams[] = $paramName; + continue; + } elseif (isset($paramInfo['default']) && $paramInfo['default']) { + $requestParams[$paramName] = $paramInfo['default']; + } + } + + if (isset($requestParams[$paramName])) { + if (isset($paramInfo['multiple']) && $paramInfo['multiple']) { + if (!is_array($requestParams[$paramName])) { + $paramValue = $requestParams[$paramName]; + $requestParams[$paramName] = []; + $requestParams[$paramName][] = $paramValue; + } + } else { + if (is_array($requestParams[$paramName])) { + $requestParams[$paramName] = end($requestParams[$paramName]); + } + } + } else { + $requestParams[$paramName] = null; + } + } + + if ($emptyRequireParams) { + throw new RuntimeException("Params '" . implode("', '", $emptyRequireParams) . "' is require"); + } + return $requestParams; + } +} diff --git a/app/Kernel/Application.php b/app/Kernel/Application.php new file mode 100644 index 0000000..6823c47 --- /dev/null +++ b/app/Kernel/Application.php @@ -0,0 +1,123 @@ + + */ +class Application +{ + private readonly string $defaultCommand; + private readonly string $defaultAction; + + public function __construct( + private readonly Request $request, + private readonly string $controllersNamespace, + private readonly Logger $logger, + ) { + $this->defaultCommand = 'help'; + $this->defaultAction = 'index'; + } + + public function run(): void + { + $requestParams = $this->request->getRequestParams(); + $config = $this->fetchConfig($requestParams); + unset($requestParams['config']); + reset($requestParams); + if (key($requestParams) !== 0) { + $commandId = $this->defaultCommand; + $actionId = $this->defaultAction; + } else { + $commandId = (string) array_shift($requestParams); + if (key($requestParams) !== 0) { + $actionId = $this->defaultAction; + } else { + $actionId = (string) array_shift($requestParams); + } + } + + if (isset($config[$commandId][$actionId])) { + $requestParams = array_merge($config[$commandId][$actionId], $requestParams); + } + + /** @var AbstractCommand $commandClass */ + $commandClass = $this->controllersNamespace . '\\' . ucfirst($commandId); + $subActions = $commandClass::getSubActions($actionId); + foreach ($subActions as $subActionId) { + if (isset($config[$commandId][$subActionId])) { + $requestParams = array_merge($config[$commandId][$subActionId], $requestParams); + } + } + /** @see AbstractCommand::__construct */ + $command = new $commandClass( + application: $this, + shellHelper: new ShellHelper(logger: $this->logger), + logger: $this->logger, + requestParams: $requestParams, + id: $commandId, + actionId: $actionId, + ); + $command->run(); + } + + private function fetchConfig(array $requestParams): array + { + if (!isset($requestParams['config'])) { + return []; + } + + $configPath = $requestParams['config']; + if (!file_exists($configPath)) { + throw new RuntimeException("Config file '{$configPath}' not found"); + } + if (!is_readable($configPath)) { + throw new RuntimeException("Config file '{$configPath}' not readable"); + } + $configFileInfo = new SplFileInfo($configPath); + $extension = $configFileInfo->getExtension(); + if ($extension === 'yaml') { + if (!function_exists('yaml_parse_file')) { + throw new RuntimeException("yaml extension not loaded"); + } + $config = yaml_parse_file($configPath); + } elseif ($extension === 'json') { + if (!function_exists('json_decode')) { + throw new RuntimeException("json extension not loaded"); + } + $json_string = file_get_contents($configPath); + $config = json_decode($json_string, true); + } elseif ($extension === 'php') { + $config = require $configPath; + } else { + throw new RuntimeException("Unknown config type {$extension}"); + } + + if (!is_array($config)) { + throw new RuntimeException("Can't parse config '{$configPath}'"); + } + + return $config; + } + + public function end($status = 0): never + { + exit($status); + } +} diff --git a/app/Mougrim/Deployer/Kernel/Request.php b/app/Kernel/Request.php similarity index 59% rename from app/Mougrim/Deployer/Kernel/Request.php rename to app/Kernel/Request.php index d96ffe6..6947a80 100644 --- a/app/Mougrim/Deployer/Kernel/Request.php +++ b/app/Kernel/Request.php @@ -1,51 +1,44 @@ + * @author Mougrim */ class Request { - private $rawRequest; - private $requestParams; + private array $requestParams; /** * @param array $rawRequest $argv */ - public function setRawRequest(array $rawRequest) - { - if ($this->rawRequest !== null) { - throw new \RuntimeException("rawRequest is already set"); - } - $this->rawRequest = $rawRequest; + public function __construct( + private readonly array $rawRequest, + ) { } /** - * @return array $argv + * @return array $argv */ - public function getRawRequest() + public function getRawRequest(): array { - if ($this->rawRequest === null) { - throw new \RuntimeException("rawRequest is not set"); - } - return $this->rawRequest; } /** - * @return array + * @return array */ - public function getRequestParams() + public function getRequestParams(): array { - if ($this->requestParams === null) { + if (!isset($this->requestParams)) { $this->requestParams = $this->populateRequestParams(); } return $this->requestParams; } - public function getRequestParam($name, $defaultValue = null) + public function getRequestParam(string|int $name, ?string $defaultValue = null): ?string { $requestParams = $this->getRequestParams(); if (!isset($requestParams[$name]) && !array_key_exists($name, $requestParams)) { @@ -55,7 +48,7 @@ public function getRequestParam($name, $defaultValue = null) return $requestParams[$name]; } - private function populateRequestParams() + private function populateRequestParams(): array { $rawRequest = $this->getRawRequest(); array_shift($rawRequest); diff --git a/app/Logger/Logger.php b/app/Logger/Logger.php new file mode 100644 index 0000000..895efdf --- /dev/null +++ b/app/Logger/Logger.php @@ -0,0 +1,101 @@ + + */ +class Logger implements LoggerInterface +{ + private array $logLevels; + private int $minLevelIndex; + + public function __construct( + private readonly string $logFilePath, + private readonly string $minLevel = LogLevel::INFO, + ) { + $reflectionClass = new ReflectionClass(LogLevel::class); + $this->logLevels = array_reverse(array_values($reflectionClass->getConstants())); + $this->minLevelIndex = array_search($this->minLevel, $this->logLevels) ?: 0; + } + + public function emergency(Stringable|string $message, array $context = []): void + { + $this->log(LogLevel::EMERGENCY, $message, $context); + } + + public function alert(Stringable|string $message, array $context = []): void + { + $this->log(LogLevel::ALERT, $message, $context); + } + + public function critical(Stringable|string $message, array $context = []): void + { + $this->log(LogLevel::CRITICAL, $message, $context); + } + + public function error(Stringable|string $message, array $context = []): void + { + $this->log(LogLevel::ERROR, $message, $context); + } + + public function warning(Stringable|string $message, array $context = []): void + { + $this->log(LogLevel::WARNING, $message, $context); + } + + public function notice(Stringable|string $message, array $context = []): void + { + $this->log(LogLevel::NOTICE, $message, $context); + } + + public function info(Stringable|string $message, array $context = []): void + { + $this->log(LogLevel::INFO, $message, $context); + } + + public function debug(Stringable|string $message, array $context = []): void + { + $this->log(LogLevel::DEBUG, $message, $context); + } + + public function log($level, Stringable|string $message, array $context = []): void + { + $index = array_search($level, $this->logLevels, true); + if ($index !== false && $index < $this->minLevelIndex) { + return; + } + + $pid = posix_getpid(); + $date = date('Y-m-d H:i:s'); + $user = $_SERVER['USER'] ?? ''; + foreach ($context as $key => $value) { + if ($value instanceof JsonSerializable) { + $context[$key] = $value->jsonSerialize(); + continue; + } + if ($value instanceof Stringable) { + $context[$key] = $value->__toString(); + continue; + } + } + $contextString = var_export($context, true); + $fullMessage = "{$pid} [{$date}] {$user} {$level} {$message} {$contextString}\n"; + + file_put_contents($this->logFilePath, $fullMessage); + echo $fullMessage; + } +} diff --git a/app/Mougrim/Deployer/Command/Help.php b/app/Mougrim/Deployer/Command/Help.php deleted file mode 100644 index eef5319..0000000 --- a/app/Mougrim/Deployer/Command/Help.php +++ /dev/null @@ -1,112 +0,0 @@ - - */ -class Help extends AbstractCommand -{ - static public function getInfo() - { - return 'this command'; - } - - static public function getRawRequestParamsInfo() - { - return [ - 'defaultParams' => [ - 0 => [ - 'info' => 'action name', - ], - ], - ]; - } - - protected $loggerName = 'help'; - - public function actionIndex() - { - $this->getLogger()->info("Usage: mougrim-deployer.php [] []"); - $this->getLogger()->info("Commands list:"); - foreach (static::getAvailableCommands() as $commandName) { - /** @var AbstractCommand $commandClass */ - $commandClass = '\Mougrim\Deployer\Command\\' . ucfirst($commandName); - $this->getLogger()->info("\t" . $commandName . "\t" . $commandClass::getInfo()); - } - } - - public function __call($methodName, array $params = []) - { - if (substr($methodName, 0, 6) !== 'action') { - throw new \RuntimeException('Call to undefined method ' . get_class() . '::' . $methodName . '()'); - } - - $commandName = substr($methodName, 6); - /** @var AbstractCommand $commandClass */ - $commandClass = '\Mougrim\Deployer\Command\\' . $commandName; - $commandName = lcfirst($commandName); - if (!class_exists($commandClass)) { - throw new \RuntimeException("Command '{$commandName}' not exists"); - } - $this->getLogger()->info("NAME"); - $this->getLogger()->info("\t{$commandName} - " . $commandClass::getInfo()); - $this->getLogger()->info(""); - - if ($commandClass::getDescription() !== null) { - $this->getLogger()->info("DESCRIPTION"); - foreach (explode("\n", $commandClass::getDescription()) as $descriptionLine) { - $this->getLogger()->info("\t{$descriptionLine}"); - } - $this->getLogger()->info(""); - } - - $this->getLogger()->info("ACTIONS"); - foreach ($commandClass::getActionsIdList() as $actionId) { - // todo action description - $this->getLogger()->info("\t{$actionId}" . ($actionId === 'index' ? ' [default]' : '')); - } - - $commandRequestParamsInfo = $commandClass::getRequestParamsInfo(); - $actionId = $this->getRequestParam(0); - if ($actionId === null && in_array('index', $commandClass::getActionsIdList(), true)) { - $actionId = 'index'; - } - if ($actionId !== null && isset($commandRequestParamsInfo[$actionId])) { - if ($actionId === 'index') { - $this->getLogger()->info("OPTIONS for default action {$actionId}"); - } else { - $this->getLogger()->info("OPTIONS for action {$actionId}"); - } - - foreach ($commandRequestParamsInfo[$actionId] as $paramName => $paramInfo) { - if (!is_numeric($paramName)) { - $paramName = "--{$paramName}"; - } - $this->getLogger()->info("\t{$paramName}"); - foreach(explode("\n", $paramInfo['info']) as $paramInfoLine) { - $this->getLogger()->info("\t\t{$paramInfoLine}"); - } - $this->getLogger()->info(""); - } - } - } - - static public function getActionsIdList() - { - return array_merge(parent::getActionsIdList(), static::getAvailableCommands()); - } - - static private function getAvailableCommands() - { - $availableCommands = []; - foreach (glob(__DIR__ . '/../Command/*') as $file) { - $commandName = basename($file, ".php"); - $availableCommands[] = lcfirst($commandName); - } - - return $availableCommands; - } -} diff --git a/app/Mougrim/Deployer/Helper/TemplateHelper.php b/app/Mougrim/Deployer/Helper/TemplateHelper.php deleted file mode 100644 index d43a08c..0000000 --- a/app/Mougrim/Deployer/Helper/TemplateHelper.php +++ /dev/null @@ -1,100 +0,0 @@ - - */ -class TemplateHelper -{ - private $shellHelper; - - public function setShellHelper(ShellHelper $shellHelper) - { - $this->shellHelper = $shellHelper; - } - - /** - * @return ShellHelper - */ - protected function getShellHelper() - { - return $this->shellHelper; - } - - private $logger; - - /** - * @return Logger - */ - protected function getLogger() - { - if ($this->logger === null) { - $this->logger = Logger::getLogger('template-helper'); - } - - return $this->logger; - } - - public function processTemplateToFile($user, $templatePath, array $params, $destinationPath = null) - { - if (!file_exists($templatePath)) { - throw new \RuntimeException("Template '{$templatePath}' not exists"); - } - if (!is_readable($templatePath)) { - throw new \RuntimeException("Template '{$templatePath}' not readable"); - } - - $this->getLogger()->info("Process template {$templatePath}"); - - if ($destinationPath === null) { - $destinationPath = $templatePath; - } else { - $this->getLogger()->info("Destination path {$destinationPath}"); - } - - $destinationDir = dirname($destinationPath); - if (!file_exists($destinationDir)) { - throw new \RuntimeException("Destination dir '{$destinationDir}' not exists"); - } - $this->getShellHelper()->sudo($user)->checkIsWritable($destinationDir); - if (file_exists($destinationPath)) { - $this->getShellHelper()->sudo($user)->checkIsWritable($destinationPath); - } - - $template = file_get_contents($templatePath); - $template = $this->processTemplateString($template, $params); - $this->getShellHelper()->sudo($user)->writeFile($destinationPath, $template); - $this->getLogger()->info("Process complete"); - - return $destinationDir; - } - - public function processTemplateString($string, array $params) - { - $replace = $this->getReplace($params); - - $result = strtr($string, $replace); - if (preg_match_all('/({{ .+? }})/', $result, $matches)) { - $notProcessedVariables = implode(", ", $matches[1]); - throw new \RuntimeException("Not all variables processed: {$notProcessedVariables}"); - } - - return $result; - } - - private function getReplace($params, $keyPrefix = '', $replace= []) { - foreach ($params as $key => $value) { - $key = $keyPrefix . $key; - if (is_array($value)) { - $replace = $this->getReplace($value, $key . '.', $replace); - } else { - $replace["{{ {$key} }}"] = $value; - } - } - - return $replace; - } -} diff --git a/app/Mougrim/Deployer/Kernel/AbstractCommand.php b/app/Mougrim/Deployer/Kernel/AbstractCommand.php deleted file mode 100644 index 1b82f69..0000000 --- a/app/Mougrim/Deployer/Kernel/AbstractCommand.php +++ /dev/null @@ -1,299 +0,0 @@ - - */ -class AbstractCommand -{ - protected $loggerName = 'command'; - private $logger; - - /** - * @return Logger - */ - public function getLogger() - { - if ($this->logger === null) { - $this->logger = Logger::getLogger($this->loggerName); - } - - return $this->logger; - } - - private $shellHelper; - - /** - * @return ShellHelper - */ - public function getShellHelper() - { - if ($this->shellHelper === null) { - $this->shellHelper = new ShellHelper(); - } - - return $this->shellHelper; - } - - private $id; - - /** - * @return string - */ - public function getId() - { - return $this->id; - } - - private $application; - - protected function getApplication() - { - return $this->application; - } - - public function __construct(Application $application, $id) - { - $this->application = $application; - $this->id = $id; - } - - private $actionId; - - /** - * @return string - */ - public function getActionId() - { - if ($this->actionId === null) { - throw new \RuntimeException("action not run"); - } - return $this->actionId; - } - - static public function getRawRequestParamsInfo() - { - return []; - } - - static public function getRequestParamsInfo() - { - $requestParamsInfo = static::getRawRequestParamsInfo(); - foreach (static::getActionsSubActions() as $actionId => $subActions) { - if (!isset($requestParamsInfo[$actionId])) { - $requestParamsInfo[$actionId] = []; - } - foreach ($subActions as $subActionId) { - if (isset($requestParamsInfo[$subActionId])) { - $requestParamsInfo[$actionId] = array_merge( - $requestParamsInfo[$subActionId], - $requestParamsInfo[$actionId] - ); - } - } - } - return $requestParamsInfo; - } - - static public function getInfo() - { - throw new \RuntimeException('Command can have description'); - } - - static public function getDescription() - { - return null; - } - - static public function getActionsIdList() - { - $actions = []; - $methods = get_class_methods(get_called_class()); - foreach ($methods as $method) { - if (preg_match('/^action([A-Z][a-zA-Z0-9]*)$/', $method, $matches)) { - $actions[] = lcfirst($matches[1]); - } - } - - return $actions; - } - - static public function getActionsSubActions() - { - return []; - } - - static public function getSubActions($actionId) - { - $actionsSubActions = static::getActionsSubActions(); - if (isset($actionsSubActions[$actionId])) { - return $actionsSubActions[$actionId]; - } else { - return []; - } - } - - private $requestParams; - - /** - * @return array - */ - public function getRequestParams() - { - if ($this->requestParams === null) { - throw new \RuntimeException("requestParams is not set"); - } - - return $this->requestParams; - } - - /** - * @param array $requestParams - */ - public function setRequestParams($requestParams) - { - if ($this->requestParams !== null) { - throw new \RuntimeException("requestParams is already set"); - } - - $this->requestParams = $requestParams; - } - - public function addRequestParam($name, $value) - { - if ($this->requestParams === null) { - throw new \RuntimeException("requestParams is not set"); - } - - $this->requestParams[$name] = $value; - } - - public function getRequestParam($name) - { - $requestParams = $this->getRequestParams(); - if (!$this->requestParamExists($name)) { - throw new \RuntimeException("Unknown param '{$name}'"); - } - - return $requestParams[$name]; - } - - public function requestParamExists($name) { - $requestParams = $this->getRequestParams(); - return isset($requestParams[$name]) || array_key_exists($name, $requestParams); - } - - protected function getAdditionalParams() - { - return []; - } - - private $params = []; - - public function getParams() - { - if (!isset($this->params[$this->getActionId()])) { - $params = $this->getRequestParams(); - $additionalParams = $this->getAdditionalParams(); - $subActions = array_merge(static::getSubActions($this->getActionId()), [$this->getActionId()]); - foreach ($subActions as $actionId) { - if (isset($additionalParams[$actionId])) { - $params = array_merge($params, $additionalParams[$actionId]); - } - } - $this->params[$this->getActionId()] = $params; - } - - return $this->params[$this->getActionId()]; - } - - public function getParam($name) - { - $params = $this->getParams(); - if (!$this->paramExists($name)) { - throw new \RuntimeException("Unknown param '{$name}'"); - } - - return $params[$name]; - } - - public function paramExists($name) { - $params = $this->getParams(); - return isset($params[$name]) || array_key_exists($name, $params); - } - - public function run($actionId) - { - $this->actionId = $actionId; - - $actionMethodName = 'action' . ucfirst($actionId); - - if (!in_array($actionId, static::getActionsIdList(), true)) { - throw new \RuntimeException("Unknown action '{$actionId}'"); - } - - $this->requestParams = $this->getActionRequestParams($actionId); - - $this->$actionMethodName(); - } - - private function getActionRequestParams($actionId) - { - $actionsRequestParamsInfo = static::getRequestParamsInfo(); - if (isset($actionsRequestParamsInfo[$actionId])) { - $requestParamsInfo = $actionsRequestParamsInfo[$actionId]; - } else { - $requestParamsInfo = []; - } - - if (isset($actionsRequestParamsInfo['defaultParams'])) { - foreach ($actionsRequestParamsInfo['defaultParams'] as $paramName => $paramInfo) { - if (!isset($requestParamsInfo[$paramName])) { - $requestParamsInfo[$paramName] = $paramInfo; - } - } - } - $requestParams = $this->getRequestParams(); - $unknownParams = array_diff(array_keys($requestParams), array_keys($requestParamsInfo)); - if ($unknownParams) { - throw new \RuntimeException("Unknown params '" . implode("', '", $unknownParams) . "'"); - } - - $emptyRequireParams = []; - foreach ($requestParamsInfo as $paramName => $paramInfo) { - if (!isset($requestParams[$paramName])) { - if (isset($paramInfo['require']) && $paramInfo['require']) { - $emptyRequireParams[] = $paramName; - continue; - } elseif (isset($paramInfo['default']) && $paramInfo['default']) { - $requestParams[$paramName] = $paramInfo['default']; - } - } - - if (isset($requestParams[$paramName])) { - if (isset($paramInfo['multiple']) && $paramInfo['multiple']) { - if (!is_array($requestParams[$paramName])) { - $paramValue = $requestParams[$paramName]; - $requestParams[$paramName] = []; - $requestParams[$paramName][] = $paramValue; - } - } else { - if (is_array($requestParams[$paramName])) { - $requestParams[$paramName] = end($requestParams[$paramName]); - } - } - } else { - $requestParams[$paramName] = null; - } - } - - if ($emptyRequireParams) { - throw new \RuntimeException("Params '" . implode("', '", $emptyRequireParams) . "' is require"); - } - return $requestParams; - } -} diff --git a/app/Mougrim/Deployer/Kernel/Application.php b/app/Mougrim/Deployer/Kernel/Application.php deleted file mode 100644 index f5b3da8..0000000 --- a/app/Mougrim/Deployer/Kernel/Application.php +++ /dev/null @@ -1,137 +0,0 @@ - - */ -class Application -{ - private $request; - private $defaultCommand = 'help'; - private $defaultAction = 'index'; - private $controllersNamespace; - - /** - * @return string - */ - public function getControllersNamespace() - { - if ($this->controllersNamespace === null) { - throw new \RuntimeException("controllersNamespace is not set"); - } - return $this->controllersNamespace; - } - - /** - * @param string $controllersNamespace - */ - public function setControllersNamespace($controllersNamespace) - { - if ($this->controllersNamespace !== null) { - throw new \RuntimeException("controllersNamespace is already set"); - } - $this->controllersNamespace = $controllersNamespace; - } - - public function setRequest(Request $request) - { - if ($this->request !== null) { - throw new \RuntimeException("Request is already set"); - } - - $this->request = $request; - } - - /** - * @return Request - */ - public function getRequest() - { - if ($this->request === null) { - throw new \RuntimeException("Request is not set"); - } - - return $this->request; - } - - public function run() - { - $requestParams = $this->getRequest()->getRequestParams(); - $config = $this->fetchConfig($requestParams); - unset ($requestParams['config']); - reset($requestParams); - if (key($requestParams) !== 0) { - $commandId = $this->defaultCommand; - $actionId = $this->defaultAction; - } else { - $commandId = array_shift($requestParams); - if (key($requestParams) !== 0) { - $actionId = $this->defaultAction; - } else { - $actionId = array_shift($requestParams); - } - } - - if (isset($config[$commandId][$actionId])) { - $requestParams = array_merge($config[$commandId][$actionId], $requestParams); - } - - $commandClass = $this->controllersNamespace . '\\' . ucfirst($commandId); - /** @var AbstractCommand $command */ - $command = new $commandClass($this, $commandId); - $subActions = $command::getSubActions($actionId); - foreach ($subActions as $subActionId) { - if (isset($config[$commandId][$subActionId])) { - $requestParams = array_merge($config[$commandId][$subActionId], $requestParams); - } - } - $command->setRequestParams($requestParams); - $command->run($actionId); - } - - private function fetchConfig($requestParams) - { - if (!isset($requestParams['config'])) { - return []; - } - - $configPath = $requestParams['config']; - if (!file_exists($configPath)) { - throw new \RuntimeException("Config file '{$configPath}' not found"); - } - if (!is_readable($configPath)) { - throw new \RuntimeException("Config file '{$configPath}' not readable"); - } - $configFileInfo = new \SplFileInfo($configPath); - $extension = $configFileInfo->getExtension(); - if ($extension === 'yaml') { - if (!function_exists('yaml_parse_file')) { - throw new \RuntimeException("yaml extension not loaded"); - } - $config = yaml_parse_file($configPath); - } elseif ($extension === 'json') { - if (!function_exists('json_decode')) { - throw new \RuntimeException("json extension not loaded"); - } - $json_string = file_get_contents($configPath); - $config = json_decode($json_string, true); - } elseif ($extension === 'php') { - /** @noinspection PhpIncludeInspection */ - $config = require $configPath; - } else { - throw new \RuntimeException("Unknown config type {$extension}"); - } - - if (!is_array($config)) { - throw new \RuntimeException("Can't parse config '{$configPath}'"); - } - - return $config; - } - - public function end($status = 0) - { - exit($status); - } -} diff --git a/bin/mougrim-deployer.php b/bin/mougrim-deployer.php index 261459e..6eaab53 100755 --- a/bin/mougrim-deployer.php +++ b/bin/mougrim-deployer.php @@ -1,8 +1,12 @@ #!/usr/bin/env php */ + +namespace Mougrim\Deployer; + // require composer autoloader $autoloadPath = dirname(__DIR__) . '/vendor/autoload.php'; if (file_exists($autoloadPath)) { @@ -10,23 +14,26 @@ require_once $autoloadPath; } else { /** @noinspection PhpIncludeInspection */ - require_once dirname(dirname(dirname(__DIR__))) . '/autoload.php'; + require_once dirname(__DIR__, 3) . '/autoload.php'; } use Mougrim\Deployer\Kernel\Request; use Mougrim\Deployer\Kernel\Application; -use Mougrim\Logger\Logger; +use Mougrim\Deployer\Logger\Logger; +use Throwable; -Logger::configure(require_once __DIR__ . '/../config/logger.php'); +/** @var Logger $logger */ +$logger = require __DIR__ . '/../config/logger.php'; try { - $request = new Request(); - $request->setRawRequest($argv); - $application = new Application(); - $application->setControllersNamespace('\Mougrim\Deployer\Command'); - $application->setRequest($request); + $request = new Request($argv); + $application = new Application( + request: $request, + controllersNamespace: '\Mougrim\Deployer\Command', + logger: $logger, + ); $application->run(); -} catch (Exception $exception) { - Logger::getLogger('dispatcher')->error("Uncaught exception:", $exception); +} catch(Throwable $exception) { + $logger->error('Uncaught exception', ['exception' => $exception]); exit(1); } diff --git a/composer.json b/composer.json index 6ea9fc8..3f4c117 100644 --- a/composer.json +++ b/composer.json @@ -21,12 +21,13 @@ "source": "https://github.com/mougrim/php-mougrim-deployer" }, "require": { - "php": ">=5.5.0", - "mougrim/php-logger": "2.*" + "php": ">=8.1.0", + "psr/log": "^3.0", + "ext-posix": "*" }, "autoload": { - "psr-0": { - "Mougrim\\Deployer": "app" + "psr-4": { + "Mougrim\\Deployer\\": "app" } }, "bin": [ diff --git a/composer.lock b/composer.lock index 6b6773c..b42b4bc 100644 --- a/composer.lock +++ b/composer.lock @@ -1,37 +1,37 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "hash": "a7fd313868338672473bb060e6865049", + "content-hash": "bdfe4048c0043dd169467dcf8c51acaf", "packages": [ { - "name": "mougrim/php-logger", - "version": "v2.0.0", + "name": "psr/log", + "version": "3.0.0", "source": { "type": "git", - "url": "https://github.com/mougrim/php-logger.git", - "reference": "3e03d674c7c75e46621cee7a17026aa53cd1e14a" + "url": "https://github.com/php-fig/log.git", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mougrim/php-logger/zipball/3e03d674c7c75e46621cee7a17026aa53cd1e14a", - "reference": "3e03d674c7c75e46621cee7a17026aa53cd1e14a", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", "shasum": "" }, "require": { - "php": ">=5.5.0" - }, - "require-dev": { - "ext-pcntl": "*", - "ext-runkit": "*", - "phpunit/phpunit": "3.7.*" + "php": ">=8.0.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, "autoload": { "psr-4": { - "Mougrim\\Logger\\": "src" + "Psr\\Log\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -40,16 +40,21 @@ ], "authors": [ { - "name": "mitallast", - "email": "mitallast@gmail.com" - }, - { - "name": "Mougrim", - "email": "rinat@mougrim.ru" + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" } ], - "description": "Simple and fast php logging library", - "time": "2015-09-03 19:27:34" + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.0" + }, + "time": "2021-07-14T16:46:02+00:00" } ], "packages-dev": [], @@ -59,7 +64,9 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=5.5.0" + "php": ">=8.1.0", + "ext-posix": "*" }, - "platform-dev": [] + "platform-dev": [], + "plugin-api-version": "2.3.0" } diff --git a/config/logger.php b/config/logger.php index 93324f0..14af368 100644 --- a/config/logger.php +++ b/config/logger.php @@ -1,50 +1,13 @@ */ -use Mougrim\Logger\Appender\AppenderStd; -use Mougrim\Logger\Appender\AppenderStream; -use Mougrim\Logger\Layout\LayoutPattern; -use Mougrim\Logger\Logger; -$logPath = __DIR__ . '/../logs/deployer.log'; -return [ - 'policy' => [ - 'ioError' => 'trigger_warn', - 'configurationError' => 'trigger_warn' - ], - 'renderer' => [ - 'nullMessage' => '-', - ], - 'layouts' => [ - 'console' => [ - 'class' => LayoutPattern::class, - 'pattern' => '{pid} [{date:Y-m-d H:i:s}] {global:_SERVER.USER} {logger}.{level} [{mdc}][{ndc}] {message} {ex}', - ], - ], - 'appenders' => [ - 'std_log' => [ - 'class' => AppenderStd::class, - 'layout' => 'console', - ], - 'console_log' => [ - 'class' => AppenderStream::class, - 'layout' => 'console', - 'stream' => $logPath, - ], - 'root_console_log' => [ - 'class' => AppenderStream::class, - 'layout' => 'console', - 'stream' => $logPath, - 'minLevel' => Logger::getLevelByName('info'), - ], - ], - 'loggers' => [ - 'help' => [ - 'appenders' => ['std_log'], - 'minLevel' => Logger::getLevelByName('info'), - 'addictive' => false, - ], - ], - 'root' => ['appenders' => ['std_log', 'root_console_log']], -]; +namespace Mougrim\Deployer; + +use Mougrim\Deployer\Logger\Logger; + +return new Logger( + logFilePath: __DIR__ . '/../logs/deployer.log', +);