backend/kernel/services/ModuleService.php
2024-12-02 16:25:28 +03:00

485 lines
14 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace kernel\services;
use DirectoryIterator;
use kernel\EntityRelation;
use kernel\Flash;
use kernel\helpers\Debug;
use kernel\helpers\Files;
use kernel\helpers\Manifest;
use kernel\helpers\RESTClient;
use kernel\models\Option;
use MongoDB\Driver\Session;
use ZipArchive;
class ModuleService
{
protected array $errors = [];
/**
* @param string $module
* @return false|array|string
*/
public function getModuleInfo(string $module): false|array|string
{
$info = [];
$info['path'] = $module;
if (file_exists($module . "/manifest.json")) {
$manifest = file_get_contents($module . "/manifest.json");
$manifest = Manifest::getWithVars($manifest);
$manifest = getConst($manifest);
$info = array_merge($info, $manifest);
}
return $info;
}
/**
* @return array
*/
public function getErrors(): array
{
return $this->errors;
}
/**
* @param $msg
* @return void
*/
public function addError($msg): void
{
$this->errors[] = $msg;
}
/**
* @param string $slug
* @return false|array|string
*/
public function getModuleInfoBySlug(string $slug): false|array|string
{
return $this->getModuleInfo($this->getModuleDir($slug));
}
/**
* @param string $slug
* @return bool
*/
public function isActive(string $slug): bool
{
$active_modules = Option::where("key", "active_modules")->first();
if ($active_modules) {
$modules = json_decode($active_modules->value);
foreach ($modules->modules as $mod) {
if ($mod === $slug) {
return true;
}
}
}
return false;
}
/**
* @param string $module
* @return void
*/
public function toggleModule(string $module): void
{
$active_modules_info = Option::where("key", "active_modules")->first();
$active_modules = json_decode($active_modules_info->value);
if (in_array($module, $active_modules->modules)) {
$this->deactivateModule($module);
} else {
$this->setActiveModule($module);
}
}
/**
* @param string $module
* @return bool
*/
public function setActiveModule(string $module): bool
{
$active_modules_info = Option::where("key", "active_modules")->first();
$active_modules = json_decode($active_modules_info->value);
if (in_array($module, $active_modules->modules)) {
return true;
}
$module_info = $this->getModuleInfoBySlug($module);
if (isset($module_info['dependence'])) {
$dependence_array = explode(',', $module_info['dependence']);
foreach ($dependence_array as $depend) {
if (!in_array($depend, $active_modules->modules)) {
$this->addError("first activate the $depend module");
return false;
}
}
}
$active_modules->modules[] = $module;
$this->runInitScript($this->getModuleInfoBySlug($module));
$active_modules_info->value = json_encode($active_modules, JSON_UNESCAPED_UNICODE);
$active_modules_info->save();
return true;
}
/**
* @param string $module
* @return bool
*/
public function deactivateModule(string $module): bool
{
$active_modules_info = Option::where("key", "active_modules")->first();
EntityRelation::removeEntityRelation($module);
EntityRelation::removePropertyRelation($module);
$active_modules = json_decode($active_modules_info->value);
if (!in_array($module, $active_modules->modules)) {
return true;
}
$dependence_array = $this->getDependencies();
$str_for_exception = '';
foreach ($dependence_array as $mod => $depend) {
if (in_array($module, $depend)) {
if ($str_for_exception !== '') {
$str_for_exception .= ', ';
}
$str_for_exception .= $mod;
}
}
if ($str_for_exception !== '') {
$this->addError("You can not deactivate $module module. First deactivate modules: $str_for_exception");
return false;
}
unset($active_modules->modules[array_search($module, $active_modules->modules)]);
$active_modules->modules = array_values($active_modules->modules);
$this->runDeactivateScript($this->getModuleInfoBySlug($module));
$active_modules_info->value = json_encode($active_modules, JSON_UNESCAPED_UNICODE);
$active_modules_info->save();
return true;
}
public function getModuleDir(string $slug)
{
$module_paths = Option::where("key", "module_paths")->first();
$dirs = [];
if ($module_paths) {
$path = json_decode($module_paths->value);
foreach ($path->paths as $p) {
$dirs[] = getConst($p);
}
}
foreach ($dirs as $dir) {
foreach (new DirectoryIterator($dir) as $fileInfo) {
if (basename($fileInfo->getPathname()) === $slug) {
return $fileInfo->getPathname();
}
}
}
return false;
}
/**
* @param $mod_info
* @return void
*/
public function runInitScript($mod_info): void
{
if (isset($mod_info['module_class'])) {
if (isset($mod_info['module_class_file'])) {
require_once $mod_info['module_class_file'];
}
$moduleClass = new $mod_info['module_class']();
$moduleClass->init();
}
}
/**
* @param $mod_info
* @return void
*/
public function runDeactivateScript($mod_info): void
{
if (isset($mod_info['module_class'])) {
if (isset($mod_info['module_class_file'])) {
require_once $mod_info['module_class_file'];
}
$moduleClass = new $mod_info['module_class']();
$moduleClass->deactivate();
}
}
/**
* @return array
*/
public function getActiveModules(): array
{
$modules = [];
$module_paths = Option::where("key", "module_paths")->first();
$active_modules = Option::where("key", "active_modules")->first();
$dirs = [];
if ($module_paths) {
$path = json_decode($module_paths->value);
foreach ($path->paths as $p) {
$dirs[] = getConst($p);
}
}
$active_modules = json_decode($active_modules->value, true);
foreach ($dirs as $dir) {
foreach (new DirectoryIterator($dir) as $fileInfo) {
if ($fileInfo->isDot() or !in_array($fileInfo->getFilename(), $active_modules['modules'])) continue;
$modules[] = $this->getModuleInfo($fileInfo->getPathname());
}
}
return $modules;
}
/**
* @return array
*/
public function getModulesRouts(): array
{
$routs = [];
$modules = $this->getActiveModules();
foreach ($modules as $module) {
if (isset($module['routs'])) {
$routs[] = $module['path'] . "/" . $module['routs'];
}
}
return $routs;
}
/**
* @return array
*/
public function getModulesMigrationsPaths(): array
{
$migrationsPaths = [];
$modules = $this->getActiveModules();
foreach ($modules as $module) {
if (isset($module['migration_path'])) {
$migrationsPaths[] = $module['path'] . "/" . $module['migration_path'];
}
}
return $migrationsPaths;
}
/**
* @param string $path
* @return bool
*/
public function installModule(string $path): bool
{
$zip = new ZipArchive;
$tmpModuleDir = md5(time());
$res = $zip->open(ROOT_DIR . $path);
if ($res === TRUE) {
$tmpModuleDirFull = RESOURCES_DIR . '/tmp/modules/' . $tmpModuleDir . "/";
$zip->extractTo($tmpModuleDirFull);
$zip->close();
} else {
$this->addError('unable to open zip archive');
return false;
}
if (!file_exists($tmpModuleDirFull . "app/manifest.json")) {
$this->addError('manifest.json not found');
return false;
}
$manifestJson = getConst(file_get_contents($tmpModuleDirFull . "app/manifest.json"));
$manifest = Manifest::getWithVars($manifestJson);
$fileHelper = new Files();
$fileHelper->copy_folder($tmpModuleDirFull . 'app', $manifest['app_module_path']);
if (isset($manifest['kernel_module_path'])) {
$fileHelper->copy_folder($tmpModuleDirFull . 'kernel', $manifest['kernel_module_path']);
} else {
$fileHelper->copy_folder($tmpModuleDirFull . 'kernel', KERNEL_APP_MODULES_DIR . '/' . $manifest['slug']);
}
$fileHelper->recursiveRemoveDir($tmpModuleDirFull);
return true;
}
/**
* @param string $path
* @return void
*/
public function uninstallModule(string $path): void
{
$moduleInfo = $this->getModuleInfo(APP_DIR . '/modules/' . basename($path));
if ($this->isActive($moduleInfo['slug'])) {
$this->deactivateModule($moduleInfo['slug']);
}
$fileHelper = new Files();
if (file_exists(APP_DIR . '/modules/' . $moduleInfo['slug'])) {
$fileHelper->recursiveRemoveDir(APP_DIR . '/modules/' . $moduleInfo['slug']);
}
if (file_exists(KERNEL_APP_MODULES_DIR . '/' . $moduleInfo['slug'])) {
$fileHelper->recursiveRemoveDir(KERNEL_APP_MODULES_DIR . '/' . $moduleInfo['slug']);
}
}
/**
* @param string $path
* @return void
*/
public function packModule(string $path): void
{
$moduleName = basename($path);
$tmpModuleDirFull = RESOURCES_DIR . '/tmp/ad/' . $moduleName . "/";
$fileHelper = new Files();
$fileHelper->copy_folder(ROOT_DIR . $path, $tmpModuleDirFull . 'app/');
if (file_exists(KERNEL_APP_MODULES_DIR . '/' . $moduleName)) {
$fileHelper->copy_folder(KERNEL_APP_MODULES_DIR . '/' . $moduleName, $tmpModuleDirFull . 'kernel/');
} else {
mkdir($tmpModuleDirFull . 'kernel/');
}
if (!is_dir(RESOURCES_DIR . '/tmp/modules')) {
mkdir(RESOURCES_DIR . '/tmp/modules', 0777, true);
}
$fileHelper->pack($tmpModuleDirFull, RESOURCES_DIR . '/tmp/modules/' . $moduleName . '.igm');
}
/**
* @param string $path
* @return bool
*/
public function updateModule(string $path): bool
{
$zip = new ZipArchive;
$tmpModuleDir = md5(time());
$res = $zip->open(ROOT_DIR . $path);
if ($res === TRUE) {
$tmpModuleDirFull = RESOURCES_DIR . '/tmp/modules/' . $tmpModuleDir . "/";
$zip->extractTo($tmpModuleDirFull);
$zip->close();
} else {
$this->addError('unable to open zip archive');
return false;
}
if (!file_exists($tmpModuleDirFull . "app/manifest.json")) {
$this->addError('manifest.json not found');
return false;
}
$manifestJson = getConst(file_get_contents($tmpModuleDirFull . "app/manifest.json"));
$manifest = Manifest::getWithVars($manifestJson);
$fileHelper = new Files();
$fileHelper->copy_folder($tmpModuleDirFull . 'app/manifest.json', $manifest['app_module_path'] . '/manifest.json');
if (isset($manifest['kernel_module_path'])) {
$fileHelper->copy_folder($tmpModuleDirFull . 'kernel', $manifest['kernel_module_path']);
} else {
$fileHelper->copy_folder($tmpModuleDirFull . 'kernel', KERNEL_APP_MODULES_DIR . '/' . $manifest['slug']);
}
$fileHelper->recursiveRemoveDir($tmpModuleDirFull);
return true;
}
/**
* @return array
*/
public function getDependencies(): array
{
$modules_info = $this->getActiveModules();
$dependence_array = [];
foreach ($modules_info as $mod) {
if (isset($mod['dependence'])) {
$dependence_array[$mod['slug']] = explode(',', $mod['dependence']);
}
}
return $dependence_array;
}
public function isInstall(string $slug): bool
{
$module_paths = Option::where("key", "module_paths")->first();
$dirs = [];
if ($module_paths) {
$path = json_decode($module_paths->value);
foreach ($path->paths as $p) {
$dirs[] = getConst($p);
}
}
foreach ($dirs as $dir) {
foreach (new DirectoryIterator($dir) as $fileInfo) {
if ($fileInfo->isDot()) continue;
if ($this->getModuleInfo($fileInfo->getPathname())['slug'] === $slug) {
return true;
};
}
}
return false;
}
public function isLastVersion(string $slug): bool
{
try {
$modules_info = RESTClient::request($_ENV['MODULE_SHOP_URL'] . '/api/module_shop/gb_slug');
$modules_info = json_decode($modules_info->getBody()->getContents(), true);
$mod_info = $this->getModuleInfoBySlug($slug);
foreach ($modules_info as $mod) {
if ($mod['slug'] === $mod_info['slug'] && $mod['version'] === $mod_info['version']) {
return true;
}
}
} catch (\Exception $e) {
throw new \Exception("Не удалось получить доступ к магазину модулей");
}
return false;
}
public function isKernelModule(string $slug): bool
{
$modules_info = $this->getKernelModules();
foreach ($modules_info as $mod) {
if ($mod['slug'] === $slug) {
return true;
}
}
return false;
}
public function getKernelModules(): array
{
$modules_info = [];
foreach (new DirectoryIterator(KERNEL_MODULES_DIR) as $fileInfo) {
if ($fileInfo->isDot()) continue;
$modules_info[] = $this->getModuleInfo($fileInfo->getPathname());
}
return $modules_info;
}
}