├── .gitignore ├── README.md ├── composer.json └── src └── main ├── BitrixNeverInclude.php └── Tools.php /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | /composer.lock 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Автозагрузчик модулей Битрикс, который поможет вам забыть про вызовы CModule::IncludeModule и Loader::includeModule 2 | 3 | Как использовать 4 | ---------------- 5 | 6 | 1 Установите через composer: 7 | 8 | `composer require webarchitect609/bitrix-neverinclude` 9 | 10 | 2 Если необходимо, то в init.php укажите список модулей, которые следует исключить из обработки данного автолоадера: 11 | 12 | `\WebArch\BitrixNeverInclude\BitrixNeverInclude::addExcludedModules(['foo.bar',]);` 13 | 14 | Данный пакет совместим с новыми версиями 15 | [andreyryabin/sprint.migration](https://packagist.org/packages/andreyryabin/sprint.migration): >=3.0 16 | 17 | 3 В `init.php` после подключения `vendor/autoload.php` добавьте вызов: 18 | 19 | `\WebArch\BitrixNeverInclude\BitrixNeverInclude::registerModuleAutoload();` 20 | 21 | **Больше подключать модули не нужно, за исключением некоторых ситуаций, описанных ниже.** 22 | 23 | Особенности реализации 24 | ---------------------- 25 | 26 | 1 Классы не из глобального namespace разбираются динамически и превращаются в название модуля, 27 | который тут же подключается. 28 | 29 | 2 Классы из глобальной области проверяются по маппингу "имя класса => имя модуля", для вычисления которого делается 30 | подключение всех установленных в системе модулей и производится сбор внутренних данных, которые потом кешируются. 31 | 32 | Известные ограничения 33 | -------------------------- 34 | 35 | ### При установке нового модуля 36 | 37 | Если происходит установка нового модуля, использующего классы в глобальной области, кеш маппинга 38 | "имя класса => имя модуля" будет неактуальным. Рекомендуется сбросить его по тегу следующим образом: 39 | 40 | ``` 41 | 42 | $tagCache = \Bitrix\Main\Application::getInstance()->getTaggedCache(); 43 | $tagCache->clearByTag(\WebArch\BitrixNeverInclude\BitrixNeverInclude::CACHE_TAG); 44 | 45 | ``` 46 | 47 | После сброса кеша рекомендуется вызвать 48 | 49 | `\WebArch\BitrixNeverInclude\BitrixNeverInclude::getClassMapping();` 50 | 51 | , чтобы при следующем хите уже существовал маппинг "имя класса => имя модуля". 52 | 53 | ### Функции в модулях 54 | 55 | В некоторых модулях объявляются функции, которые недоступны без подключения модуля. Например, [функция 56 | `SaleFormatCurrency`](https://dev.1c-bitrix.ru/api_help/sale/functions/saleformatcurrency.php) недоступна без 57 | подключения [модуля `sale`](https://dev.1c-bitrix.ru/api_help/sale/index.php). Из-за того, что в php нет механизма 58 | автозагрузки функций, эта проблема не может быть решена автоматически. Следует вручную подключать соответствующий модуль 59 | перед вызовом функции, объявленной в этом модуле. 60 | 61 | ### Несовместимость с некоторыми модулями 62 | 63 | Если архитектура модуля Битрикс сделана таким образом, что в `include.php` происходит регистрация своего автозагрузчика 64 | классов, то очень вероятна несовместимость такого модуля с данным пакетом. Следует исключить этот модуль из 65 | обработки(см. выше) и подключать его по необходимости вручную. 66 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webarchitect609/bitrix-neverinclude", 3 | "description": "Automatic Bitrix modules loader helps you to forget about CModule::IncludeModule and Loader::includeModule.", 4 | "homepage": "https://github.com/webarchitect609/bitrix-neverinclude", 5 | "keywords": [ 6 | "bitrix", 7 | "module", 8 | "autoloading" 9 | ], 10 | "license": "MIT", 11 | "authors": [ 12 | { 13 | "name": "Sergey Gripinskiy", 14 | "email": "web-architect@mail.ru", 15 | "role": "Owner" 16 | } 17 | ], 18 | "require": { 19 | "php": "^7.2 || ^8.0", 20 | "webarchitect609/bitrix-cache": "^1.8" 21 | }, 22 | "require-dev": { 23 | "roave/security-advisories": "dev-latest" 24 | }, 25 | "autoload": { 26 | "psr-4": { 27 | "WebArch\\BitrixNeverInclude\\": "src/main" 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/BitrixNeverInclude.php: -------------------------------------------------------------------------------- 1 | includeAllInstalledModules(); 70 | 71 | return $tools->getModuleByClassNameMapping($tools->getAutoLoadClasses()); 72 | }; 73 | 74 | return (new BitrixCache())->setTime(86400) 75 | ->setTag(self::CACHE_TAG) 76 | ->callback($closure); 77 | } 78 | 79 | /** 80 | * Подключает модуль, определяя его по имени класса. 81 | * 82 | * @param string $class 83 | * 84 | * @throws LoaderException 85 | * @throws ReflectionException 86 | */ 87 | protected function autoloadModule(string $class) 88 | { 89 | $moduleName = $this->recognizeOldModule($class); 90 | 91 | if (!$moduleName) { 92 | $moduleName = $this->recognizeNewModule($class); 93 | } 94 | 95 | if (!$moduleName) { 96 | return; 97 | } 98 | 99 | if (!self::isExcludedModule($moduleName) && Loader::includeModule($moduleName)) { 100 | Loader::autoLoad($class); 101 | } 102 | } 103 | 104 | /** 105 | * Определение модуля для старых классов из глобальной области. 106 | * 107 | * @param string $class 108 | * 109 | * @throws ReflectionException 110 | * @return string Пустая строка, если не удалось определить имя модуля 111 | */ 112 | protected function recognizeOldModule(string $class): string 113 | { 114 | if (strpos($class, '\\') !== false) { 115 | return ''; 116 | } 117 | 118 | return $this->checkClassMapping($class); 119 | } 120 | 121 | /** 122 | * @param string $class 123 | * 124 | * @throws ReflectionException 125 | * @return string 126 | */ 127 | private function checkClassMapping(string $class): string 128 | { 129 | $lowClass = strtolower(trim($class)); 130 | 131 | $classMapping = static::getClassMapping(); 132 | 133 | if (!isset($classMapping[$lowClass])) { 134 | return ''; 135 | } 136 | 137 | return (string)$classMapping[$lowClass]; 138 | } 139 | 140 | /** 141 | * Определение модуля по namespace класса 142 | * 143 | * @param string $class 144 | * 145 | * @return string Пустая строка, если не удалось определить имя модуля 146 | */ 147 | protected function recognizeNewModule($class) 148 | { 149 | $chunks = explode('\\', $class); 150 | 151 | /* 152 | * Это стандартный битриксовый модуль 153 | */ 154 | if ('Bitrix' === $chunks[0] && isset($chunks[1])) { 155 | return strtolower($chunks[1]); 156 | } 157 | 158 | /* 159 | * Иной кастомный модуль 160 | */ 161 | if (isset($chunks[0], $chunks[1])) { 162 | return strtolower($chunks[0] . '.' . $chunks[1]); 163 | } 164 | 165 | return ''; 166 | } 167 | 168 | /** 169 | * Проверяет, не является ли модуль исключённым 170 | * 171 | * @param string $moduleName 172 | * 173 | * @return bool 174 | */ 175 | public static function isExcludedModule($moduleName) 176 | { 177 | if (is_null(self::$excludedModulesIndex)) { 178 | self::$excludedModulesIndex = array_flip(self::$excludedModules); 179 | } 180 | 181 | return isset(self::$excludedModulesIndex[$moduleName]); 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/main/Tools.php: -------------------------------------------------------------------------------- 1 | getStaticProperties(); 36 | 37 | if (isset($staticProperties['autoLoadClasses'])) { 38 | return $staticProperties['autoLoadClasses']; 39 | } elseif (isset($staticProperties['arAutoLoadClasses'])) { 40 | return $staticProperties['arAutoLoadClasses']; 41 | } 42 | 43 | return []; 44 | } 45 | 46 | /** 47 | * Вернуть индекс модуля по имени класса 48 | * 49 | * @param array $autoLoadClasses 50 | * 51 | * @return array 52 | */ 53 | public function getModuleByClassNameMapping(array $autoLoadClasses) 54 | { 55 | $map = []; 56 | 57 | foreach ($autoLoadClasses as $className => $data) { 58 | 59 | /** 60 | * Нас не интересует модуль `main`, т.к. всегда подключён. 61 | * Также не интересуют классы не из глобального namespace 62 | */ 63 | if ( 64 | !isset($data['module']) 65 | || 'main' === $data['module'] 66 | || strpos($className, '\\') !== false 67 | ) { 68 | continue; 69 | } 70 | 71 | $map[$className] = $data['module']; 72 | } 73 | 74 | return $map; 75 | } 76 | } 77 | --------------------------------------------------------------------------------