From 1b6120528169064b6ee8bbbe5b8ee49270d6e985 Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Sun, 12 Jul 2020 19:11:53 +0300 Subject: [PATCH] aoolication runners --- application/Espo/Core/Application.php | 127 +++++------------- .../ApplicationRunners/ApplicationRunner.php | 37 +++++ .../Core/ApplicationRunners/ClearCache.php | 54 ++++++++ .../Espo/Core/ApplicationRunners/Cli.php | 38 ++++++ .../Espo/Core/ApplicationRunners/Command.php | 65 +++++++++ .../Espo/Core/ApplicationRunners/Cron.php | 63 +++++++++ .../Espo/Core/ApplicationRunners/Daemon.php | 96 +++++++++++++ .../Espo/Core/ApplicationRunners/Job.php | 55 ++++++++ .../Espo/Core/ApplicationRunners/Rebuild.php | 54 ++++++++ .../ApplicationRunners/SetupSystemUser.php | 38 ++++++ .../Espo/Core/Console/CommandManager.php | 15 ++- application/Espo/Core/Portal/Application.php | 11 ++ application/Espo/Core/Utils/Cron/JobTask.php | 8 +- .../Espo/Resources/metadata/app/metadata.json | 1 + clear_cache.php | 5 +- command.php | 10 +- cron.php | 5 +- daemon.php | 5 +- rebuild.php | 5 +- 19 files changed, 574 insertions(+), 118 deletions(-) create mode 100644 application/Espo/Core/ApplicationRunners/ApplicationRunner.php create mode 100644 application/Espo/Core/ApplicationRunners/ClearCache.php create mode 100644 application/Espo/Core/ApplicationRunners/Cli.php create mode 100644 application/Espo/Core/ApplicationRunners/Command.php create mode 100644 application/Espo/Core/ApplicationRunners/Cron.php create mode 100644 application/Espo/Core/ApplicationRunners/Daemon.php create mode 100644 application/Espo/Core/ApplicationRunners/Job.php create mode 100644 application/Espo/Core/ApplicationRunners/Rebuild.php create mode 100644 application/Espo/Core/ApplicationRunners/SetupSystemUser.php diff --git a/application/Espo/Core/Application.php b/application/Espo/Core/Application.php index 15b86bd999..625319378c 100644 --- a/application/Espo/Core/Application.php +++ b/application/Espo/Core/Application.php @@ -37,7 +37,6 @@ use Espo\Core\{ ContainerConfiguration, InjectableFactory, EntryPointManager, - CronManager, Authentication\Authentication, Api\Auth as ApiAuth, Api\ErrorOutput as ApiErrorOutput, @@ -49,8 +48,8 @@ use Espo\Core\{ Utils\Config, Utils\Metadata, Utils\ClientManager, + Utils\Log, ORM\EntityManager, - Console\CommandManager as ConsoleCommandManager, Portal\Application as PortalApplication, Loaders\Config as ConfigLoader, Loaders\Log as LogLoader, @@ -70,6 +69,8 @@ use Slim\{ Factory\AppFactory as SlimAppFactory, }; +use ReflectionClass; + /** * A central access point of the application. */ @@ -79,6 +80,8 @@ class Application protected $slim = null; + protected $log; + protected $loaderClassNames = [ 'config' => ConfigLoader::class, 'log' => LogLoader::class, @@ -118,7 +121,7 @@ class Application $route = $item['route']; if (!in_array($method, $crudList) && $method !== 'options') { - $GLOBALS['log']->error("Route: Method '{$method}' does not exist. Check the route '{$route}'."); + $this->getLog()->error("Route: Method '{$method}' does not exist. Check the route '{$route}'."); continue; } @@ -234,97 +237,46 @@ class Application } /** - * Run cron. + * Run an application through a specific runner. + * You can find runner classes in `Espo\Core\ApplicationRunners`. */ - public function runCron() + public function run(string $runnerName, ?object $params = null) { - if ($this->getConfig()->get('cronDisabled')) { - $GLOBALS['log']->warning("Cron is not run because it's disabled with 'cronDisabled' param."); - return; - } - $this->setupSystemUser(); - $this->getCronManager()->run(); - } + $className = $this->getRunnerClassName($runnerName); - /** - * Run daemon. - */ - public function runDaemon() - { - $maxProcessNumber = $this->getConfig()->get('daemonMaxProcessNumber'); - $interval = $this->getConfig()->get('daemonInterval'); - $timeout = $this->getConfig()->get('daemonProcessTimeout'); - - $phpExecutablePath = $this->getConfig()->get('phpExecutablePath'); - if (!$phpExecutablePath) { - $phpExecutablePath = (new \Symfony\Component\Process\PhpExecutableFinder)->find(); - } - - if (!$maxProcessNumber || !$interval) { - $GLOBALS['log']->error("Daemon config params are not set."); + if (!$className) { + $this->getLog()->error("Application runner '{$runnerName}' does not exist."); return; } - $processList = []; + $class = new ReflectionClass($className); - while (true) { - $toSkip = false; - $runningCount = 0; - foreach ($processList as $i => $process) { - if ($process->isRunning()) { - $runningCount++; - } else { - unset($processList[$i]); - } + if ($class->getStaticPropertyValue('cli', false)) { + if (substr(php_sapi_name(), 0, 3) !== 'cli') { + die("Application runner '{$runnerName}' can be run only via CLI."); } - $processList = array_values($processList); - if ($runningCount >= $maxProcessNumber) { - $toSkip = true; - } - if (!$toSkip) { - $process = new \Symfony\Component\Process\Process([$phpExecutablePath, 'cron.php']); - $process->setTimeout($timeout); - $process->run(); - $processList[] = $process; - } - sleep($interval); } + if ($class->getStaticPropertyValue('setupSystemUser', false)) { + $this->setupSystemUser(); + } + + $runner = $this->getInjectableFactory()->create($className); + + $runner->run($params); } - /** - * Run a job by ID. A job record should exist in database. - */ - public function runJob(string $id) + protected function getRunnerClassName(string $runnerName) : ?string { - $this->setupSystemUser(); - $this->getCronManager()->runJobById($id); - } + $className = 'Espo\\Core\\ApplicationRunners\\' . ucfirst($runnerName); - /** - * Rebuild application. - */ - public function runRebuild() - { - $this->getDataManager()->rebuild(); - } + if (!class_exists($className)) { + $className = $this->getMetadata()->get(['app', 'runners', $runnerName, 'className']); + if (!$className || !class_exists($className)) { + return null; + } + } - /** - * Clear application cache. - */ - public function runClearCache() - { - $this->getDataManager()->clearCache(); - } - - /** - * Run command in Console Command framework. - */ - public function runCommand(string $command) - { - $this->setupSystemUser(); - - $consoleCommandManager = $this->getInjectableFactory()->create(ConsoleCommandManager::class); - return $consoleCommandManager->run($command); + return $className; } /** @@ -354,6 +306,11 @@ class Application return $this->container->get('injectableFactory'); } + protected function getLog() : Log + { + return $this->container->get('log'); + } + protected function getClientManager() : ClientManager { return $this->container->get('clientManager'); @@ -369,16 +326,6 @@ class Application return $this->container->get('config'); } - protected function getDataManager() : DataManager - { - return $this->container->get('dataManager'); - } - - protected function getCronManager() : CronManager - { - return $this->container->get('cronManager'); - } - protected function getEntityManager() : EntityManager { return $this->container->get('entityManager'); diff --git a/application/Espo/Core/ApplicationRunners/ApplicationRunner.php b/application/Espo/Core/ApplicationRunners/ApplicationRunner.php new file mode 100644 index 0000000000..2dd9e125cd --- /dev/null +++ b/application/Espo/Core/ApplicationRunners/ApplicationRunner.php @@ -0,0 +1,37 @@ +dataManager = $dataManager; + } + + public function run() + { + $this->dataManager->clearCache(); + } +} diff --git a/application/Espo/Core/ApplicationRunners/Cli.php b/application/Espo/Core/ApplicationRunners/Cli.php new file mode 100644 index 0000000000..a657d58197 --- /dev/null +++ b/application/Espo/Core/ApplicationRunners/Cli.php @@ -0,0 +1,38 @@ +injectableFactory = $injectableFactory; + + $this->commandManager = $this->injectableFactory->create(ConsoleCommandManager::class); + } + + public function run() + { + ob_start(); + + $result = $this->commandManager->run($_SERVER['argv']); + + if (is_string($result)) { + ob_end_clean(); + echo $result; + } + } +} diff --git a/application/Espo/Core/ApplicationRunners/Cron.php b/application/Espo/Core/ApplicationRunners/Cron.php new file mode 100644 index 0000000000..fd6856bdef --- /dev/null +++ b/application/Espo/Core/ApplicationRunners/Cron.php @@ -0,0 +1,63 @@ +cronManager = $cronManager; + $this->config = $config; + } + + public function run() + { + if ($this->config->get('cronDisabled')) { + $GLOBALS['log']->warning("Cron is not run because it's disabled with 'cronDisabled' param."); + return; + } + + $this->cronManager->run(); + } +} diff --git a/application/Espo/Core/ApplicationRunners/Daemon.php b/application/Espo/Core/ApplicationRunners/Daemon.php new file mode 100644 index 0000000000..e9d0bf6a48 --- /dev/null +++ b/application/Espo/Core/ApplicationRunners/Daemon.php @@ -0,0 +1,96 @@ +config = $config; + } + + public function run() + { + $maxProcessNumber = $this->config->get('daemonMaxProcessNumber'); + $interval = $this->config->get('daemonInterval'); + $timeout = $this->config->get('daemonProcessTimeout'); + + $phpExecutablePath = $this->config->get('phpExecutablePath'); + if (!$phpExecutablePath) { + $phpExecutablePath = (new PhpExecutableFinder)->find(); + } + + if (!$maxProcessNumber || !$interval) { + $GLOBALS['log']->error("Daemon config params are not set."); + return; + } + + $processList = []; + + while (true) { + $toSkip = false; + $runningCount = 0; + foreach ($processList as $i => $process) { + if ($process->isRunning()) { + $runningCount++; + } else { + unset($processList[$i]); + } + } + $processList = array_values($processList); + if ($runningCount >= $maxProcessNumber) { + $toSkip = true; + } + if (!$toSkip) { + $process = new Process([$phpExecutablePath, 'cron.php']); + $process->setTimeout($timeout); + $process->run(); + $processList[] = $process; + } + sleep($interval); + } + } +} diff --git a/application/Espo/Core/ApplicationRunners/Job.php b/application/Espo/Core/ApplicationRunners/Job.php new file mode 100644 index 0000000000..2f00078a6f --- /dev/null +++ b/application/Espo/Core/ApplicationRunners/Job.php @@ -0,0 +1,55 @@ +cronManager = $cronManager; + } + + public function run(object $params) + { + $this->cronManager->runJobById($params->id); + } +} diff --git a/application/Espo/Core/ApplicationRunners/Rebuild.php b/application/Espo/Core/ApplicationRunners/Rebuild.php new file mode 100644 index 0000000000..aedbfb24ff --- /dev/null +++ b/application/Espo/Core/ApplicationRunners/Rebuild.php @@ -0,0 +1,54 @@ +dataManager = $dataManager; + } + + public function run() + { + $this->dataManager->rebuild(); + } +} diff --git a/application/Espo/Core/ApplicationRunners/SetupSystemUser.php b/application/Espo/Core/ApplicationRunners/SetupSystemUser.php new file mode 100644 index 0000000000..4df667cf59 --- /dev/null +++ b/application/Espo/Core/ApplicationRunners/SetupSystemUser.php @@ -0,0 +1,38 @@ +metadata = $metadata; } - public function run(string $command) + public function run(array $argv) { + $command = isset($argv[1]) ? trim($argv[1]) : null; + + if (!$command) { + $msg = "Command name is not specifed."; + echo $msg . "\n"; + throw new Error($msg); + } + $command = ucfirst(Util::hyphenToCamelCase($command)); - $params = $this->getParams($_SERVER['argv']); + $params = $this->getParams($argv); $options = $params['options']; $flagList = $params['flagList']; diff --git a/application/Espo/Core/Portal/Application.php b/application/Espo/Core/Portal/Application.php index bafe5dd9f8..de7b3e3d2e 100644 --- a/application/Espo/Core/Portal/Application.php +++ b/application/Espo/Core/Portal/Application.php @@ -110,6 +110,17 @@ class Application extends BaseApplication return $routeList; } + protected function getRunnerClassName(string $runnerName) : ?string + { + $className = 'Espo\\Core\\Portal\\ApplicationRunners\\' . ucfirst($runnerName); + + if (class_exists($className)) { + return $className; + } + + return parent::getRunnerClassName($runnerName); + } + public function runClient() { $this->container->get('clientManager')->display(null, null, [ diff --git a/application/Espo/Core/Utils/Cron/JobTask.php b/application/Espo/Core/Utils/Cron/JobTask.php index 0863a455a9..3ace136215 100644 --- a/application/Espo/Core/Utils/Cron/JobTask.php +++ b/application/Espo/Core/Utils/Cron/JobTask.php @@ -29,6 +29,8 @@ namespace Espo\Core\Utils\Cron; +use Espo\Core\Application; + class JobTask extends \Spatie\Async\Task { private $jobId; @@ -44,9 +46,11 @@ class JobTask extends \Spatie\Async\Task public function run() { - $app = new \Espo\Core\Application(); + $app = new Application(); try { - $app->runJob($this->jobId); + $app->run('job', (object) [ + 'id' => $this->jobId, + ]); } catch (\Throwable $e) { $GLOBALS['log']->error("JobTask: Failed job run. Job id: ".$this->jobId.". Error details: ".$e->getMessage()); } diff --git a/application/Espo/Resources/metadata/app/metadata.json b/application/Espo/Resources/metadata/app/metadata.json index d70514a30c..fd7e82003c 100644 --- a/application/Espo/Resources/metadata/app/metadata.json +++ b/application/Espo/Resources/metadata/app/metadata.json @@ -13,6 +13,7 @@ ["app", "templateHelpers"], ["app", "appParams"], ["app", "cleanup"], + ["app", "runners"], ["app", "auth2FAMethods", "__ANY__", "implementationClassName"], ["app", "auth2FAMethods", "__ANY__", "implementationUserClassName"], ["authenticationMethods", "__ANY__", "implementationClassName"] diff --git a/clear_cache.php b/clear_cache.php index 11299ea70a..fcf8d453fb 100644 --- a/clear_cache.php +++ b/clear_cache.php @@ -27,9 +27,6 @@ * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ -if (substr(php_sapi_name(), 0, 3) != 'cli') die('ClearCache can be run only via CLI.'); - include "bootstrap.php"; -$app = new \Espo\Core\Application(); -$app->runClearCache(); +(new \Espo\Core\Application())->run('clearCache'); diff --git a/command.php b/command.php index 665e2d3d2a..4ba76747fd 100644 --- a/command.php +++ b/command.php @@ -27,19 +27,13 @@ * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ -if (substr(php_sapi_name(), 0, 3) != 'cli') exit; +include "bootstrap.php"; ob_start(); -$command = isset($_SERVER['argv'][1]) ? trim($_SERVER['argv'][1]) : null; -if (empty($command)) exit; - -include "bootstrap.php"; -$app = new \Espo\Core\Application(); -$result = $app->runCommand($command); +$result = (new \Espo\Core\Application())->run('command'); if (is_string($result)) { ob_end_clean(); echo $result; } -exit; diff --git a/cron.php b/cron.php index beba8bc9a3..7ac8e6138e 100644 --- a/cron.php +++ b/cron.php @@ -27,9 +27,6 @@ * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ -if (substr(php_sapi_name(), 0, 3) != 'cli') die('Cron can be run only via CLI.'); - include "bootstrap.php"; -$app = new \Espo\Core\Application(); -$app->runCron(); +(new \Espo\Core\Application())->run('cron'); diff --git a/daemon.php b/daemon.php index 95e79718cc..3c9c9e5580 100644 --- a/daemon.php +++ b/daemon.php @@ -27,9 +27,6 @@ * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ -if (substr(php_sapi_name(), 0, 3) != 'cli') die('Daemon can be run only via CLI.'); - include "bootstrap.php"; -$app = new \Espo\Core\Application(); -$app->runDaemon(); +(new \Espo\Core\Application())->run('daemon'); diff --git a/rebuild.php b/rebuild.php index 69a1c18269..7c36636501 100644 --- a/rebuild.php +++ b/rebuild.php @@ -27,9 +27,6 @@ * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ -if (substr(php_sapi_name(), 0, 3) != 'cli') die('Rebuild can be run only via CLI.'); - include "bootstrap.php"; -$app = new \Espo\Core\Application(); -$app->runRebuild(); +(new \Espo\Core\Application())->run('rebuild');