diff --git a/ModuleCloner.php b/ModuleCloner.php new file mode 100644 index 0000000..9b83e33 --- /dev/null +++ b/ModuleCloner.php @@ -0,0 +1,199 @@ +copyStructure(); + $this->fixContent(); + } + + /** + * Копирует структуру папок и файлов из источника в цель. + */ + protected function copyStructure(): void + { + $this->copyFolderSafe( + $this->sourceEntity->getPath(), + $this->targetEntity->getPath() + ); + } + + /** + * Рекурсивное копирование папки с фильтрацией и динамическим переименованием. + */ + protected function copyFolderSafe(string $source, string $destination): void + { + if ($this->shouldExclude($source)) { + echo "Skip: $source" . PHP_EOL; + return; + } + + if (is_dir($source)) { + $destination = $this->ensureDirectory($destination); + if ($destination === null) { + return; + } + + $dir = dir($source); + while (false !== ($entry = $dir->read())) { + if ($entry !== '.' && $entry !== '..') { + $srcPath = $source . DIRECTORY_SEPARATOR . $entry; + $dstPath = $destination . DIRECTORY_SEPARATOR . $entry; + + $dstPath = $this->renamePathSegment($dstPath); + + $this->copyFolderSafe($srcPath, $dstPath); + } + } + $dir->close(); + } else { + $this->copyFileSafe($source, $destination); + } + } + + /** + * Копирует файл с сохранением времени модификации. + */ + protected function copyFileSafe(string $source, string $destination): void + { + $time = filemtime($source); + if (file_exists($destination) && filemtime($destination) >= $time) { + return; + } + + if (copy($source, $destination)) { + touch($destination, $time); + } + } + + /** + * Создает директорию, если она отсутствует. + */ + protected function ensureDirectory(string $path): ?string + { + if (file_exists($path)) { + if (is_dir($path)) { + return $path; + } + unlink($path); + } + return mkdir($path, 0777, true) ? $path : null; + } + + /** + * Выполняет массовую замену строк в PHP файлах целевого модуля. + */ + protected function fixContent(): void + { + $iterator = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($this->targetEntity->getPath()), + RecursiveIteratorIterator::LEAVES_ONLY + ); + + foreach ($iterator as $file) { + if ($file->isFile() && $file->getExtension() === 'php') { + $path = $file->getPathname(); + $content = file_get_contents($path); + $original = $content; + + $replacements = $this->getReplacements(); + foreach ($replacements as $search => $replace) { + $content = str_replace($search, $replace, $content); + } + + if ($original !== $content) { + file_put_contents($path, $content); + echo "Fixed: $path" . PHP_EOL; + } + } + } + } + + /** + * Формирует карту замен на основе сущностей модулей. + */ + protected function getReplacements(): array + { + $s = $this->sourceEntity; + $t = $this->targetEntity; + + $productS = ucfirst($s->getProductCode()); + $productT = ucfirst($t->getProductCode()); + $companyS = ucfirst($s->getCompanyCode()); + $companyT = ucfirst($t->getCompanyCode()); + + return [ + $s->getLangPrefix() => $t->getLangPrefix(), + $s->getId() => $t->getId(), + $s->getPrefix() => $t->getPrefix(), + $s->getNamespace() => $t->getNamespace(), + "$productS: $productS" => "$productT: $productT", + $productS => $productT, + $companyS => $companyT, + $s->getProductCode() => $t->getProductCode(), + $s->getCompanyCode() => $t->getCompanyCode(), + ]; + } + + /** + * Переименовывает сегменты пути (папки) в соответствии с именами модулей. + */ + protected function renamePathSegment(string $path): string + { + $sourceLast = collect(explode('.', $this->sourceEntity->getId()))->last(); + $targetLast = collect(explode('.', $this->targetEntity->getId()))->last(); + + $path = str_replace(ucfirst($sourceLast), ucfirst($targetLast), $path); + $path = str_replace(strtolower($sourceLast), strtolower($targetLast), $path); + + return $path; + } + + /** + * Проверяет, должен ли файл/папка быть исключен из копирования. + */ + protected function shouldExclude(string $path): bool + { + foreach ($this->excludePatterns as $pattern) { + if (preg_match($pattern, $path)) { + return true; + } + } + return false; + } +} diff --git a/clone.php b/clone.php index adf9e43..9e86cd4 100644 --- a/clone.php +++ b/clone.php @@ -1,122 +1,36 @@ read() ) ) { - if ( $entry != '.' && $entry != '..' ) - copy_folder_safe( "$d1/$entry", "$d2/$entry", $upd, $force, $exclude, $handler); - } - $d->close(); - } - else { - $ok = copy_safe( $d1, $d2, $upd ); - } +if (!isset($argv[1])) { + echo "Error: Please provide the target module path as the first argument." . PHP_EOL; + exit(1); } -function mkdir_safe( $dir, $force ) { - if (file_exists($dir)) { - if (is_dir($dir)) return $dir; - elseif (!$force) return false; - unlink($dir); +try { + $sourcePath = __DIR__; + $targetPath = realpath($argv[1]); + + if ($targetPath === false) { + throw new InvalidArgumentException("Invalid target path: {$argv[1]}"); } - return (mkdir($dir, 0777, true)) ? $dir : false; + + $targetEntity = __getModuleEntity($targetPath); + $sourceEntity = __getModuleEntity($sourcePath); + + echo "Cloning {$sourceEntity->getId()} -> {$targetEntity->getId()}..." . PHP_EOL; + + $cloner = new ModuleCloner($sourceEntity, $targetEntity); + $cloner->clone(); + + echo "Success: Module cloned successfully." . PHP_EOL; +} catch (Throwable $e) { + echo "Critical Error: " . $e->getMessage() . PHP_EOL; + exit(1); } - -function copy_safe ($f1, $f2, $upd) { - $time1 = filemtime($f1); - if (file_exists($f2)) { - $time2 = filemtime($f2); - if ($time2 >= $time1 && $upd) return false; - } - $ok = copy($f1, $f2); - if ($ok) touch($f2, $time1); - return $ok; -} - -function fs_log($str) { - return; - $log = fopen('./fs_log.txt', 'a'); - $time = date('Y-m-d H:i:s'); - fwrite($log, "$str ($time)\n"); - fclose($log); -} - -function _fix(ModuleEntity $newEntity, ModuleEntity $nullableEntity){ - $d = new RecursiveDirectoryIterator( $newEntity->getPath() ); - foreach( new RecursiveIteratorIterator( $d, 1 ) as $path ){ - if( is_file( $path ) && str_ends_with($path, '.php')){ - $orig_file = file_get_contents($path); - - $new_file = $orig_file; - $cr = 0; - $new_file = str_replace($nullableEntity->getLangPrefix(), $newEntity->getLangPrefix(), $new_file, $cr); - $new_file = str_replace($nullableEntity->getId(), $newEntity->getId(), $new_file, $cr); - $new_file = str_replace($nullableEntity->getPrefix(), $newEntity->getPrefix(), $new_file, $cr); - $new_file = str_replace($nullableEntity->getNamespace(), $newEntity->getNamespace(), $new_file, $cr); - - $new_file = str_replace( - ucfirst($nullableEntity->getProductCode()) . ': ' . ucfirst($nullableEntity->getProductCode()), - ucfirst($newEntity->getProductCode()) . ': ' . ucfirst($newEntity->getProductCode()), - $new_file, - $cr - ); - - $new_file = str_replace(ucfirst($nullableEntity->getProductCode()), ucfirst($newEntity->getProductCode()), $new_file, $cr); - $new_file = str_replace(ucfirst($nullableEntity->getCompanyCode()), ucfirst($newEntity->getCompanyCode()), $new_file, $cr); - $new_file = str_replace($nullableEntity->getProductCode(), $newEntity->getProductCode(), $new_file, $cr); - $new_file = str_replace($nullableEntity->getCompanyCode(), $newEntity->getCompanyCode(), $new_file, $cr); - - - - echo $cr . ' ' .$path . PHP_EOL; - - if($orig_file != $new_file){ - file_put_contents($path, $new_file); - } - } - } -} - -echo $newEntity->getId() . PHP_EOL; - - -copy_folder_safe($nullableEntity->getPath(), $newEntity->getPath(), true, true, [ - '/\.git$/', - '/\.module\.json/', - '/clone\.php/', - '/readme\.md/', - '/src\/\.env$/', - '/src\/config\.php$/', -], function ($d1, &$d2) use ($nullableEntity, $newEntity){ - $lastModuleName = collect(explode('.', $newEntity->fields()->id))->last(); - $lastNullableModuleName = collect(explode('.', $nullableEntity->fields()->id))->last(); - - - $d2 = str_replace(ucfirst($lastNullableModuleName), ucfirst($lastModuleName), $d2, $cr); - $d2 = str_replace(strtolower($lastNullableModuleName), strtolower($lastModuleName), $d2, $cr); -}); - -_fix($newEntity, $nullableEntity); diff --git a/install/index.php b/install/index.php index 1e0c863..26de80b 100644 --- a/install/index.php +++ b/install/index.php @@ -1,13 +1,14 @@ module['deps'])) { foreach ($this->module['deps'] as $moduleId) { - if (!\Bitrix\Main\Loader::includeModule($moduleId)) { + if (!Loader::includeModule($moduleId)) { $APPLICATION->ThrowException("Module {$moduleId} not installed!"); return false; } } } - if (!CheckVersion(PHP_VERSION, '5.4.0')) { - $APPLICATION->ThrowException('Php version required more 5.4.0'); + if (!(version_compare(PHP_VERSION, '8.0.0') >= 0)) { + $APPLICATION->ThrowException('Php version required more 8.0.0'); return false; } @@ -70,7 +71,7 @@ class bx_nullable extends \CModule public function InstallCatalogExport() { - if (is_dir(__DIR__ . '/catalog_export') && \Bitrix\Main\Loader::includeModule('catalog')) { + if (is_dir(__DIR__ . '/catalog_export') && Loader::includeModule('catalog')) { CopyDirFiles(__DIR__ . '/catalog_export', $_SERVER['DOCUMENT_ROOT'] . CATALOG_PATH2EXPORTS, true, true); } return true; diff --git a/lib/Collection/NullableCollection.php b/lib/Collection/NullableCollection.php index bbdb823..2dbdcc0 100644 --- a/lib/Collection/NullableCollection.php +++ b/lib/Collection/NullableCollection.php @@ -25,7 +25,7 @@ class NullableCollection extends AEntityCollection */ public function &get($id, $default = null) { - return parent::get($id, $default); // TODO: Change the autogenerated stub + return parent::get($id, $default); } /** @@ -33,7 +33,7 @@ class NullableCollection extends AEntityCollection */ public function &first(callable $callback = null, $default = null) { - return parent::first($callback, $default); // TODO: Change the autogenerated stub + return parent::first($callback, $default); } /** @@ -41,7 +41,7 @@ class NullableCollection extends AEntityCollection */ public function &last() { - return parent::last(); // TODO: Change the autogenerated stub + return parent::last(); } /** @@ -49,7 +49,7 @@ class NullableCollection extends AEntityCollection */ public function shift() { - return parent::shift(); // TODO: Change the autogenerated stub + return parent::shift(); } /** @@ -57,7 +57,7 @@ class NullableCollection extends AEntityCollection */ public function pop() { - return parent::pop(); // TODO: Change the autogenerated stub + return parent::pop(); } /** @@ -65,7 +65,7 @@ class NullableCollection extends AEntityCollection */ public function filter($callback = null) { - return parent::filter($callback); // TODO: Change the autogenerated stub + return parent::filter($callback); } /** @@ -73,6 +73,6 @@ class NullableCollection extends AEntityCollection */ public function all() { - return parent::all(); // TODO: Change the autogenerated stub + return parent::all(); } } \ No newline at end of file diff --git a/lib/Entity/NullableEntity.php b/lib/Entity/NullableEntity.php index f46eef4..3c109b5 100644 --- a/lib/Entity/NullableEntity.php +++ b/lib/Entity/NullableEntity.php @@ -13,7 +13,7 @@ class NullableEntity extends AEntity */ public function getFactory() { - return parent::getFactory(); // TODO: Change the autogenerated stub + return parent::getFactory(); } /** diff --git a/lib/Factory/NullableFactory.php b/lib/Factory/NullableFactory.php index 147aa60..f3a0638 100644 --- a/lib/Factory/NullableFactory.php +++ b/lib/Factory/NullableFactory.php @@ -53,7 +53,7 @@ class NullableFactory extends AORMFactory */ public function getCollection($filter = [], $order = [], $select = [], $limit = null, $offset = null) { - return parent::getCollection($filter, $order, $select, $limit, $offset); // TODO: Change the autogenerated stub + return parent::getCollection($filter, $order, $select, $limit, $offset); } /** @@ -62,7 +62,7 @@ class NullableFactory extends AORMFactory */ public function getEntityByIds(array $arIds) { - return parent::getEntityByIds($arIds); // TODO: Change the autogenerated stub + return parent::getEntityByIds($arIds); } /**