Current Path : /var/www/www-root/data/www/monolith-realty.ru/bitrix/modules/main/lib/ |
Current File : /var/www/www-root/data/www/monolith-realty.ru/bitrix/modules/main/lib/loader.php |
<?php namespace Bitrix\Main; use Bitrix\Main\DI\ServiceLocator; /** * Class Loader loads required files, classes and modules. It is the only class which is included directly. * @package Bitrix\Main */ class Loader { /** * Can be used to prevent loading all modules except main and fileman */ const SAFE_MODE = false; const BITRIX_HOLDER = "bitrix"; const LOCAL_HOLDER = "local"; protected static $safeModeModules = ["main" => true, "fileman" => true]; protected static $loadedModules = ["main" => true]; protected static $semiloadedModules = []; protected static $modulesHolders = ["main" => self::BITRIX_HOLDER]; protected static $sharewareModules = []; /** * Custom autoload paths. * @var array [namespace => [ [path1, depth1], [path2, depth2] ] */ protected static $namespaces = []; /** * Returned by includeSharewareModule() if module is not found */ const MODULE_NOT_FOUND = 0; /** * Returned by includeSharewareModule() if module is installed */ const MODULE_INSTALLED = 1; /** * Returned by includeSharewareModule() if module works in demo mode */ const MODULE_DEMO = 2; /** * Returned by includeSharewareModule() if the trial period is expired */ const MODULE_DEMO_EXPIRED = 3; protected static $autoLoadClasses = []; /** * @var bool Controls throwing exception by requireModule method */ protected static $requireThrowException = true; /** @deprecated */ const ALPHA_LOWER = "qwertyuioplkjhgfdsazxcvbnm"; /** @deprecated */ const ALPHA_UPPER = "QWERTYUIOPLKJHGFDSAZXCVBNM"; /** * Includes a module by its name. * * @param string $moduleName Name of the included module * @return bool Returns true if module was included successfully, otherwise returns false * @throws LoaderException */ public static function includeModule($moduleName) { if (!is_string($moduleName) || $moduleName == "") { throw new LoaderException("Empty module name"); } if (preg_match("#[^a-zA-Z0-9._]#", $moduleName)) { throw new LoaderException(sprintf("Module name '%s' is not correct", $moduleName)); } $moduleName = strtolower($moduleName); if (self::SAFE_MODE) { if (!isset(self::$safeModeModules[$moduleName])) { return false; } } if (isset(self::$loadedModules[$moduleName])) { return self::$loadedModules[$moduleName]; } if (isset(self::$semiloadedModules[$moduleName])) { trigger_error("Module '".$moduleName."' is in loading progress", E_USER_WARNING); } $arInstalledModules = ModuleManager::getInstalledModules(); if (!isset($arInstalledModules[$moduleName])) { return (self::$loadedModules[$moduleName] = false); } $documentRoot = self::getDocumentRoot(); $moduleHolder = self::LOCAL_HOLDER; $pathToInclude = $documentRoot."/".$moduleHolder."/modules/".$moduleName; if (!file_exists($pathToInclude)) { $moduleHolder = self::BITRIX_HOLDER; $pathToInclude = $documentRoot."/".$moduleHolder."/modules/".$moduleName; if (!file_exists($pathToInclude)) { return (self::$loadedModules[$moduleName] = false); } } //register a PSR-4 base folder for the module if (strpos($moduleName, ".") !== false) { //partner's module $baseName = str_replace(".", "\\", ucwords($moduleName, ".")); } else { //bitrix's module $baseName = "Bitrix\\".ucfirst($moduleName); } self::registerNamespace($baseName, $documentRoot."/".$moduleHolder."/modules/".$moduleName."/lib"); self::$modulesHolders[$moduleName] = $moduleHolder; if (class_exists('\Dev\Main\Migrator\ModuleUpdater')) { \Dev\Main\Migrator\ModuleUpdater::checkUpdates($moduleName, $pathToInclude); } $res = true; if (file_exists($pathToInclude."/include.php")) { //recursion control self::$semiloadedModules[$moduleName] = true; $res = self::includeModuleInternal($pathToInclude."/include.php"); unset(self::$semiloadedModules[$moduleName]); } self::$loadedModules[$moduleName] = ($res !== false); if (!self::$loadedModules[$moduleName]) { //unregister the namespace if "include" fails self::unregisterNamespace($baseName); } else { ServiceLocator::getInstance()->registerByModuleSettings($moduleName); } return self::$loadedModules[$moduleName]; } /** * Includes module by its name, throws an exception in case of failure * * @param string $moduleName * * @return bool * @throws LoaderException */ public static function requireModule($moduleName) { $included = self::includeModule($moduleName); if (!$included && self::$requireThrowException) { throw new LoaderException("Required module `{$moduleName}` was not found"); } return $included; } private static function includeModuleInternal($path) { /** @noinspection PhpUnusedLocalVariableInspection */ global $DB, $MESS; return include_once($path); } /** * Includes shareware module by its name. * Module must initialize constant <module name>_DEMO = Y in include.php to define demo mode. * include.php must return false to define trial period expiration. * Constants are used because it is easy to obfuscate them. * * @param string $moduleName Name of the included module * @return int One of the following constant: Loader::MODULE_NOT_FOUND, Loader::MODULE_INSTALLED, Loader::MODULE_DEMO, Loader::MODULE_DEMO_EXPIRED */ public static function includeSharewareModule($moduleName) { if (isset(self::$sharewareModules[$moduleName])) { return self::$sharewareModules[$moduleName]; } $module = str_replace(".", "_", $moduleName); if (self::includeModule($moduleName)) { if (defined($module."_DEMO") && constant($module."_DEMO") == "Y") { self::$sharewareModules[$moduleName] = self::MODULE_DEMO; } else { self::$sharewareModules[$moduleName] = self::MODULE_INSTALLED; } return self::$sharewareModules[$moduleName]; } if (defined($module."_DEMO") && constant($module."_DEMO") == "Y") { return (self::$sharewareModules[$moduleName] = self::MODULE_DEMO_EXPIRED); } return (self::$sharewareModules[$moduleName] = self::MODULE_NOT_FOUND); } public static function clearModuleCache($moduleName) { if (!is_string($moduleName) || $moduleName == "") { throw new LoaderException("Empty module name"); } if ($moduleName !== "main") { unset(self::$loadedModules[$moduleName]); unset(self::$modulesHolders[$moduleName]); } unset(self::$sharewareModules[$moduleName]); } /** * Returns document root * * @return string Document root */ public static function getDocumentRoot() { static $documentRoot = null; if ($documentRoot === null) { $documentRoot = rtrim($_SERVER["DOCUMENT_ROOT"], "/\\"); } return $documentRoot; } /** * Registers classes for auto-loading. * All the frequently used classes should be registered for auto-loading (performance). * It is not necessary to register rarely used classes. They can be found and loaded dynamically. * * @param string $moduleName Name of the module. Can be null if classes are not part of any module * @param array $classes Array of classes with class names as keys and paths as values. * @throws LoaderException */ public static function registerAutoLoadClasses($moduleName, array $classes) { if (empty($classes)) { return; } if (($moduleName !== null) && empty($moduleName)) { throw new LoaderException(sprintf("Module name '%s' is not correct", $moduleName)); } foreach ($classes as $class => $file) { $class = ltrim($class, "\\"); $class = strtolower($class); self::$autoLoadClasses[$class] = [ "module" => $moduleName, "file" => $file, ]; } } /** * Registers namespaces with custom paths. * e.g. ('Bitrix\Main\Dev', '/home/bitrix/web/site/bitrix/modules/main/dev/lib') * * @param string $namespace A namespace prefix. * @param string $path An absolute path. */ public static function registerNamespace($namespace, $path) { $namespace = trim($namespace, "\\")."\\"; $namespace = strtolower($namespace); $path = rtrim($path, "/\\"); $depth = substr_count(rtrim($namespace, "\\"), "\\"); self::$namespaces[$namespace][] = [ "path" => $path, "depth" => $depth, ]; } /** * Unregisters a namespace. * @param string $namespace */ public static function unregisterNamespace($namespace) { $namespace = trim($namespace, "\\")."\\"; $namespace = strtolower($namespace); unset(self::$namespaces[$namespace]); } /** * Registers an additional autoload handler. * @param callable $handler */ public static function registerHandler(callable $handler) { \spl_autoload_register($handler); } /** * PSR-4 compatible autoloader. * https://www.php-fig.org/psr/psr-4/ * * @param string $className */ public static function autoLoad($className) { // fix web env $className = ltrim($className, "\\"); $classLower = strtolower($className); static $documentRoot = null; if ($documentRoot === null) { $documentRoot = self::getDocumentRoot(); } //optimization via direct paths if (isset(self::$autoLoadClasses[$classLower])) { $pathInfo = self::$autoLoadClasses[$classLower]; if ($pathInfo["module"] != "") { $module = $pathInfo["module"]; $holder = (self::$modulesHolders[$module] ?? self::BITRIX_HOLDER); $filePath = (defined('REPOSITORY_ROOT') && $holder === self::BITRIX_HOLDER) ? REPOSITORY_ROOT : "{$documentRoot}/{$holder}/modules"; $filePath .= '/'.$module."/".$pathInfo["file"]; require_once($filePath); } else { require_once($documentRoot.$pathInfo["file"]); } return; } if (preg_match("#[^\\\\/a-zA-Z0-9_]#", $className)) { return; } $tryFiles = [[ "real" => $className, "lower" => $classLower, ]]; if (substr($classLower, -5) == "table") { // old *Table stored in reserved files $tryFiles[] = [ "real" => substr($className, 0, -5), "lower" => substr($classLower, 0, -5), ]; } foreach ($tryFiles as $classInfo) { $classParts = explode("\\", $classInfo["lower"]); //remove class name array_pop($classParts); while (!empty($classParts)) { //go from the end $namespace = implode("\\", $classParts)."\\"; if (isset(self::$namespaces[$namespace])) { //found foreach (self::$namespaces[$namespace] as $namespaceLocation) { $depth = $namespaceLocation["depth"]; $path = $namespaceLocation["path"]; $fileParts = explode("\\", $classInfo["real"]); for ($i=0; $i <= $depth; $i++) { array_shift($fileParts); } $classPath = implode("/", $fileParts); $classPathLower = strtolower($classPath); // final path lower case $filePath = $path.'/'.$classPathLower.".php"; if (file_exists($filePath)) { require_once($filePath); break 3; } // final path original case $filePath = $path.'/'.$classPath.".php"; if (file_exists($filePath)) { require_once($filePath); break 3; } } } //try the shorter namespace array_pop($classParts); } } } /** * @param string $className * * @throws LoaderException */ public static function requireClass($className) { $file = ltrim($className, "\\"); // fix web env $file = strtolower($file); if (preg_match("#[^\\\\/a-zA-Z0-9_]#", $file)) return; $tryFiles = [$file]; if (substr($file, -5) == "table") { // old *Table stored in reserved files $tryFiles[] = substr($file, 0, -5); } foreach ($tryFiles as $file) { $file = str_replace('\\', '/', $file); $arFile = explode("/", $file); if ($arFile[0] === "bitrix") { array_shift($arFile); if (empty($arFile)) { break; } $module = array_shift($arFile); if ($module == null || empty($arFile)) { break; } } else { $module1 = array_shift($arFile); $module2 = array_shift($arFile); if ($module1 == null || $module2 == null || empty($arFile)) { break; } $module = $module1.".".$module2; } if (!self::includeModule($module)) { throw new LoaderException(sprintf( "There is no `%s` class, module `%s` is unavailable", $className, $module )); } } self::autoLoad($className); } /** * Checks if file exists in /local or /bitrix directories * * @param string $path File path relative to /local/ or /bitrix/ * @param string|null $root Server document root, default self::getDocumentRoot() * @return string|bool Returns combined path or false if the file does not exist in both dirs */ public static function getLocal($path, $root = null) { if ($root === null) { $root = self::getDocumentRoot(); } if (file_exists($root."/local/".$path)) { return $root."/local/".$path; } elseif (file_exists($root."/bitrix/".$path)) { return $root."/bitrix/".$path; } else { return false; } } /** * Checks if file exists in personal directory. * If $_SERVER["BX_PERSONAL_ROOT"] is not set than personal directory is equal to /bitrix/ * * @param string $path File path relative to personal directory * @return string|bool Returns combined path or false if the file does not exist */ public static function getPersonal($path) { $root = self::getDocumentRoot(); $personal = ($_SERVER["BX_PERSONAL_ROOT"] ?? ""); if ($personal <> '' && file_exists($root.$personal."/".$path)) { return $root.$personal."/".$path; } return self::getLocal($path, $root); } /** * Changes requireModule behavior * * @param bool $requireThrowException */ public static function setRequireThrowException($requireThrowException) { self::$requireThrowException = (bool) $requireThrowException; } } class LoaderException extends \Exception { public function __construct($message = "", $code = 0, \Exception $previous = null) { parent::__construct($message, $code, $previous); } }